Introduction —

The human parasitic nematode Strongyloides stercoralis is estimated to infect approximately 610 million people (Buonfrate et al 2020). Similar to other intestinal parasitic nematodes, S. stercoralis has a complex life cycle in which distinct developmental stages navigate starkly different environments via life-stage specific behavioral preferences ( Castelletto et al 2014, Bryant and Hallem 2018a ). Across nematode species, ethological and behavioral differences are often reflected in the temporal regulation of gene expression. For S. stercoralis, several studies have utilized bulk RNA sequencing to probe the genomic basis of parasitism by identifying gene families that are uniquely upregulated in parasitic life stages ( Stolzfus et al 2012, Hunt et al 2016, Hunt et al 2018). These efforts highlight a feature of modern bioinformatics - the secondary analysis of publically avaliable datasets. Here, an original dataset featuring bulk RNA sequencing of seven S. stercoralis developmental stages was initially aligned to genomic contigs (6 December 2011 draft, Stolzfus et al 2012 ). Subsequently, a subset of this dataset was re-analyzed coincident with the release of a high-quality draft assembly ( Hunt et al 2016 ); this re-analysis focused on the differences between three life stages: free-living adults that navigate the environment, parasitic adults located within the host intestine, and infective third-stage larvae that actively engage in host-seeking behaviors. As genome assembly and RNA sequencing of additional parasitic nematode species continues, the publically-avaliable S. stercoralis RNAseq dataset continues to be utilized for cross-species and cross-life stage comparisons ( Hunt et al 2016, Hunt et al 2018 ). Furthermore, several helminth RNA-seq datasets, including S. stercoralis were realigned to their reference genomes and integrated into WormBase Parasite, where they are accessible to the field at large in the form of genome-aligned RNA-seq expression tracks ( Howe et al 2017 ).

As research into the genomic basis of parasitism in helminths continues, access to quantitative gene expression levels will greatly enhance studies into the functional role of specific genes and gene families. Differential gene expression results have been published as supplemental data ( Hunt et al 2016 ), however these analyses only included pairwise comparisons between three life stages. Quantitative comparisons that utilize the full seven avaliable life stages have not yet been published.

The introduction of alignment-free read mapping tools such as Kallisto has significantly lowered the computational barrier for re-analysis of publically avaliable RNAseq datasets. Here, I take advantage of these tools to re-analyze the S. sterocoralis bulk RNA-seq data, extending differential gene expression analyses across all avaliable developmental stages. Kallisto and custom R scripts were used to perform ultra-fast read mapping of raw reads to the S. stercoalis reference transcriptome (PRJEB528.WBPS14.mRNA_transcripts, downloaded from WormBase Parasite on 16 June 2020); raw count data was saved as a digitial gene expression list. Raw reads were quantified as counts per million using the EdgeR package, then filtered to remove trnscripts with low counts (less than 1 count-per-million in at least 3 samples), and normalized using the trimmed mean of M-values method (TMM, Robinson and Oshlack ) to permit between-samples comparisons. Principal component analysis applied to this data identified two developmental clusters that account for xx% and xx% of expression variability between S. stercoralis developmental stages. The limma package ( Ritchie et al 2015, Phipson et al 2016) was used to conduct pairwise differential gene expression analyses between life stages or developmental clusters. The mean-vairance relationship was modeled using a precision wights approach Law et al 2014.

[In progress: Finally, a Shiny Web App interface was created to enable non-technical users to access these quantitative analyses. The site includes two modes for data exploration. First, a Gene Search mode allows users to download counts and differential expression analysis results for specific genes of interest. Second, a Differential Expression Browser mode enables users to make pair-wise comparisons between individual development stages or PCA-identified clusters, then visualize and download lists of differentially expressed genes that pass user-specified thresholds for magnitude and significance (logFC and Benjamini-Hochberg adjusted p-values, respectively).]

Kallisto read mapping —

# This script checks the qualitiy of the fastq files and performs an alignment to the Strongyloides stercoralis cDNA transcriptome reference with Kallisto.
# To run this 'shell script' you will need to open your terminal and navigate to the directory where this script resides on your computer.
# This should be the same directory where you fastq files and reference fasta file are found.
# Change permissions on your computer so that you can run a shell script by typing: 'chmod u+x readMapping.sh' (without the quotes) at the terminal prompt 
# Then type './readMapping.sh' (without the quotes) at the prompt.  
# This will begin the process of running each line of code in the shell script.

# first use fastqc to check the quality of our fastq files:
fastqc *.gz -t 14

# next, we want to build an index from our reference fasta file 
# I got my reference Strongyloides sterocralis transcripts from WormBase Parasite
kallisto index -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.fa

# now map reads to the indexed reference host transcriptome
# use as many 'threads' as your machine will allow in order to speed up the read mapping process.
# note that we're also including the '&>' at the end of each line
# this takes the information that would've been printed to our terminal, and outputs this in a log file that is saved in /data/course_data

# Free-Living Females
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146941 -t 14 ERR146941_1.fastq.gz ERR146941_2.fastq.gz&> ERR146941.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146942 -t 14 ERR146942_1.fastq.gz ERR146942_2.fastq.gz&> ERR146942.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146943 -t 14 ERR146943_1.fastq.gz ERR146943_2.fastq.gz&> ERR146943.log

# L3i+ (Activated iL3s)
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146944 -t 14 ERR146944_1.fastq.gz ERR146944_2.fastq.gz&> ERR146944.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146945 -t 14 ERR146945_1.fastq.gz ERR146945_2.fastq.gz&> ERR146945.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146946 -t 14 ERR146946_1.fastq.gz ERR146946_2.fastq.gz&> ERR146946.log

# L3i
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146947 -t 14 ERR146947_1.fastq.gz ERR146947_2.fastq.gz&> ERR146947.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146948 -t 14 ERR146948_1.fastq.gz ERR146948_2.fastq.gz&> ERR146948.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146949 -t 14 ERR146949_1.fastq.gz ERR146949_2.fastq.gz&> ERR146949.log

# Post-Free-Living L1s
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146950 -t 14 ERR146950_1.fastq.gz ERR146950_2.fastq.gz&> ERR146950.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146951 -t 14 ERR146951_1.fastq.gz ERR146951_2.fastq.gz&> ERR146951.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146952 -t 14 ERR146952_1.fastq.gz ERR146952_2.fastq.gz&> ERR146952.log

# Post-Parasitic L1s
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146953 -t 14 ERR146953_1.fastq.gz ERR146953_2.fastq.gz&> ERR146953.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146954 -t 14 ERR146954_1.fastq.gz ERR146954_2.fastq.gz&> ERR146954.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146955 -t 14 ERR146955_1.fastq.gz ERR146955_2.fastq.gz&> ERR146955.log

# Post-Parasitic L1s
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146956 -t 14 ERR146956_1.fastq.gz ERR146956_2.fastq.gz&> ERR146956.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146957 -t 14 ERR146957_1.fastq.gz ERR146957_2.fastq.gz&> ERR146957.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146958 -t 14 ERR146958_1.fastq.gz ERR146958_2.fastq.gz&> ERR146958.log

# Parasitic Females
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146959 -t 14 ERR146959_1.fastq.gz ERR146959_2.fastq.gz&> ERR146959.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146960 -t 14 ERR146960_1.fastq.gz ERR146960_2.fastq.gz&> ERR146960.log
kallisto quant -i strongyloides_stercoralis.PRJEB528.WBPS14.mRNA_transcripts.index -o ERR146961 -t 14 ERR146961_1.fastq.gz ERR146961_2.fastq.gz&> ERR146961.log



# summarize fastqc and kallisto mapping results in a single summary html using MultiQC
multiqc -d . 

echo "Finished"

Import Kallisto reads into R —

# load packages ----
suppressPackageStartupMessages({
  library(tidyverse) 
  library(tximport)
  library(ensembldb)
  library(biomaRt)
  library(magrittr)
})
# read in the study design ----
targets <- read_tsv("./Data/PRJEB3116_study_design.txt",
                    na = c("", "NA", "na"))
# create file paths to the abundance files generated by Kallisto using the 'file.path' function
path <- file.path("./Data",targets$sample, "abundance.tsv")
# check to make sure this path is correct by seeing if the files exist
#all(file.exists(path)) 

# get annotations using organism-specific package ----
Tx.Ss <- getBM(attributes=c('wbps_transcript_id',
                            'wbps_gene_id'),
               # grab the ensembl annotations for Wormbase Parasite genes
               mart = useMart(biomart="parasite_mart", 
                              dataset = "wbps_gene", 
                              host="https://parasite.wormbase.org", 
                              port = 443),
               filters = c('species_id_1010'),
               value = list('ststerprjeb528')) %>%
  as_tibble() %>%
  #we need to rename the columns retreived from biomart
  dplyr::rename(target_id = wbps_transcript_id,
                gene_name = wbps_gene_id) 


# import Kallisto transcript counts into R using Tximport ----
# copy the abundance files to the working directory and rename so that each sample has a unique name
Txi_gene <- tximport(path, 
                     type = "kallisto", 
                     tx2gene = Tx.Ss[,1:2], 
                     txOut = FALSE, #How does the result change if this =FALSE vs =TRUE?
                     countsFromAbundance = "lengthScaledTPM",
                     ignoreTxVersion = FALSE)

Annotate Genes with C. elegans homology and gene Description Terms —

# Introduction to this chunk -----------
# This chunk imports gene annotation information for S. stercoralis genes, including:
# C. elegans homologs, percent homology, and any Interprot or general Description information using biomart.
# It will save a table

# Load packages ------
library(biomaRt) # annotate genes using bioMart
#library(biomartr) # extending biomart annotation language


# Get C. elegans homologs for S. stercoralis genes from BioMart and filter -----
Annt.import <- getBM(attributes=c('wbps_gene_id', 'caelegprjna13758_gene_name',
                                  'caelegprjna13758_homolog_perc_id_r1',
                                  'description',
                                  'interpro_short_description',
                                  'go_name_1006',
                                  'uniprot_sptrembl'),
                     # grab the ensembl annotations for Wormbase Parasite genes
                     mart = useMart(biomart="parasite_mart", 
                                    dataset = "wbps_gene", 
                                    host="https://parasite.wormbase.org", 
                                    port = 443),
                     filters = c('species_id_1010'),
                     value = list('ststerprjeb528')) %>%
  as_tibble() %>%
  #we need to rename the two columns we just retreived from biomart
  dplyr::rename(geneID = wbps_gene_id,
                Ce_geneID = caelegprjna13758_gene_name,
                Description = description,
                percent_homology = caelegprjna13758_homolog_perc_id_r1,
                GO_term = go_name_1006,
                UniProtKB = uniprot_sptrembl
  ) %>%
  dplyr::group_by(geneID)

# Replace empty string values (mostly in Ce_geneID column) with NAs
Annt.import[Annt.import == ""]<-NA

# Remove any duplications in the possible C. elegans gene homolog matches. Select based on highest % homology.
Annt.import$percent_homology[is.na(Annt.import$percent_homology)] <- 1000 #Give fake value here to make sure genes without a Ce homolog aren't filtered out

Annt.logs <-Annt.import %>%
  dplyr::select(!c(interpro_short_description:GO_term))%>%
  group_by(geneID) %>%
  slice_max(n = 1, order_by = percent_homology, with_ties = FALSE) %>%
  group_by(geneID, Ce_geneID) 

# Remove source code to shorten the description
Annt.logs$Description<- Annt.logs$Description %>%
  str_replace_all(string = ., pattern = "  \\[Source:.*\\]", replacement = "") %>%
  cbind()

Annt.logs$percent_homology[Annt.logs$percent_homology == 1000] <- NA

# Clean up interprotKB terms, removing duplications and collapsing to one line
Annt.interpro<-Annt.import %>%
  dplyr::select(geneID, Ce_geneID, interpro_short_description) %>%
  group_by(geneID, Ce_geneID) %>%
  dplyr::distinct(interpro_short_description, .keep_all = TRUE) %>%
  dplyr::summarise(InterPro = paste(interpro_short_description, 
                                    collapse = ', ')) 
# Clean up GO terms, removing duplications and collapsing to one line
Annt.goterms<-Annt.import %>%
  dplyr::select(geneID, Ce_geneID, GO_term) %>%
  group_by(geneID, Ce_geneID) %>%
  dplyr::distinct(GO_term, .keep_all = TRUE) %>%
  dplyr::summarise(GO_term = paste(GO_term, collapse = ', '))

annotations<-dplyr::left_join(Annt.logs, Annt.interpro) %>%
  dplyr::left_join(.,Annt.goterms) %>%
  ungroup() %>%
  column_to_rownames(var = "geneID")

Data Filtering and Normalization —

# Goals of this chunk:
# 1 - Filter and normalize data
# 2 - use ggplot2 to visualize the impact of filtering and normalization on the data.

# Notes:
# recall that abundance data are TPM, while the counts are read counts mapping to each gene or transcript

# Load packages -----
suppressPackageStartupMessages({
  library(tidyverse)
  library(edgeR) 
  library(matrixStats)
  library(cowplot)
  library(ggthemes)
  library(RColorBrewer)
})

# Generate and plot summary stats for the data ----
myTPM.stats <- transform(Txi_gene$abundance, 
                         SD=rowSds(Txi_gene$abundance), 
                         AVG=rowMeans(Txi_gene$abundance),
                         MED=rowMedians(Txi_gene$abundance))

# produce a scatter plot of the transformed data
p1<-ggplot(myTPM.stats) + 
  aes(x = SD, y = MED) +
  geom_point(shape=16, size=2, alpha = 0.2) +
  geom_smooth(method=lm) +
  #geom_hex(show.legend = FALSE) +
  labs(y="Median", x = "Standard deviation",
       title="Transcripts per million (TPM)",
       subtitle="unfiltered, non-normalized data",
       caption="S. stercoralis Stoltzfus RNAseq Dataset") +
  theme_bw()
p1

# make a Digital Gene Expression list using the raw counts and plot ----
myDGEList <- DGEList(Txi_gene$counts, 
                     samples = targets$sample, 
                     group = targets$group,
                     genes = annotations)

# calculate and plot log2 counts per million ----

# capture sample labels from the study design file
sampleLabels <- targets$sample

# capture group labels from the study design file
groupLabels <- targets$group

# Generate life stage IDs
ids <- rep(cbind(targets$group), 
           times = nrow(Tx.Ss)) %>%
  as_factor()

# use the 'cpm' function from EdgeR to get log2 counts per million
# then coerce into a tibble
# add sample names to the dataframe
# tidy up the dataframe into a tibble
log2.cpm.df.pivot <-cpm(myDGEList, log=TRUE) %>%
  as_tibble(rownames = "geneID") %>%
  setNames(nm = c("geneID", sampleLabels)) %>%
  pivot_longer(cols = -geneID, 
               names_to = "samples", 
               values_to = "expression") %>% 
  add_column(life_stage = ids)


# plot the pivoted data
p2 <- ggplot(log2.cpm.df.pivot) +
  aes(x=samples, y=expression, fill=life_stage) +
  geom_violin(trim = FALSE, show.legend = T, alpha= 0.7) +
  stat_summary(fun = "median", 
               geom = "point", 
               shape = 20, 
               size = 2, 
               color = "black", 
               show.legend = FALSE) +
  labs(y="log2 expression", x = "sample",
       title="Log2 Counts per Million (CPM)",
       subtitle="unfiltered, non-normalized",
       caption=paste0("produced on ", Sys.time())) +
  theme_bw() +
  scale_fill_brewer(palette = "Dark2") +
  coord_flip()
p2

# Filter the data ----
# Take a look at how many genes or transcripts have no read counts in all of the samples
#table(rowSums(myDGEList$counts==0)==nrow(targets))

# now set some cut-off to get rid of genes/transcripts with low counts
# again using rowSums to tally up the 'TRUE' results of a simple evaluation
# how many genes had more than 1 CPM (TRUE) in at least n samples
# The cutoff "n" should be adjusted for the number of samples in the smallest group of comparison.
keepers <- cpm(myDGEList) %>%
  rowSums(.>1)>=3

# now use base R's simple subsetting method to filter the DGEList based on the logical produced above
myDGEList.filtered <- myDGEList[keepers,]
#dim(myDGEList.filtered)

ids.filtered <- rep(cbind(targets$group), 
                    times = nrow(myDGEList.filtered)) %>%
  as_factor()

log2.cpm.filtered.df.pivot <- cpm(myDGEList.filtered, log=TRUE) %>%
  as_tibble(rownames = "geneID") %>%
  setNames(nm = c("geneID", sampleLabels)) %>%
  pivot_longer(cols = -geneID,
               names_to = "samples",
               values_to = "expression") %>%
  add_column(life_stage = ids.filtered)

p3 <- ggplot(log2.cpm.filtered.df.pivot) +
  aes(x=samples, y=expression, fill=life_stage) +
  geom_violin(trim = FALSE, show.legend = T, alpha= 0.7) +
  stat_summary(fun = "median", 
               geom = "point", 
               shape = 20, 
               size = 2, 
               color = "black", 
               show.legend = FALSE) +
  labs(y="log2 expression", x = "sample",
       title="Log2 Counts per Million (CPM)",
       subtitle="filtered, non-normalized",
       caption=paste0("produced on ", Sys.time())) +
  theme_bw() +
  scale_fill_brewer(palette = "Dark2") +
  coord_flip()
p3

# Normalize the data using a between samples normalization ----
# Source for TMM sample normalization here: https://genomebiology.biomedcentral.com/articles/10.1186/gb-2010-11-3-r25
myDGEList.filtered.norm <- calcNormFactors(myDGEList.filtered, method = "TMM")

log2.cpm.filtered.norm.df<- cpm(myDGEList.filtered.norm, log=TRUE) %>%
  as_tibble(rownames = "geneID") %>%
  setNames(nm = c("geneID", sampleLabels))

log2.cpm.filtered.norm.df.pivot<-log2.cpm.filtered.norm.df %>%
  pivot_longer(cols = -geneID,
               names_to = "samples",
               values_to = "expression") %>%
  add_column(life_stage = ids.filtered)

p4 <- ggplot(log2.cpm.filtered.norm.df.pivot) +
  aes(x=samples, y=expression, fill=life_stage) +
  geom_violin(trim = FALSE, show.legend = T, alpha = 0.7) +
  stat_summary(fun = "median", 
               geom = "point", 
               shape = 20, 
               size = 2, 
               color = "black", 
               show.legend = FALSE) +
  labs(y="log2 expression", x = "sample",
       title="Log2 Counts per Million (CPM)",
       subtitle="filtered, TMM normalized",
       caption=paste0("produced on ", Sys.time())) +
  theme_bw() +
  scale_fill_brewer(palette = "Dark2") +
  coord_flip()
p4

# Plot all the plots on a grid
p5 <- plot_grid(p1, p2, p3, p4, labels = c('A', 'B', 'C', 'D'), label_size = 12)
# ggsave("Strongyloides stercoralis RNAseq Counts.pdf", 
#        plot = p5, 
#        device = "pdf",
#        height = 8.5,
#        width = 11,
#        path = './Outputs/')
# 

Hierarchical Clustering and Principle Components Analysis —

# Introduction to this chunk -----------
# This code chunk starts with filtered and normalized abundance data in a data frame (not tidy).
# It will implement hierarchical clustering and PCA analyses on the data.
# It will plot various graphs and can save them in PDF files.
# Load packages ------
suppressPackageStartupMessages({
  library(tidyverse) # you're familiar with this fromt the past two lectures
  library(ggplot2)
  library(RColorBrewer)
  library(ggdendro)
  library(magrittr)
  library(factoextra)
  library(gridExtra)
  library(cowplot)
  library(dendextend)
})

# Identify variables of interest in study design file ----
group <- factor(targets$group)

# Hierarchical clustering ---------------
# Remember: hierarchical clustering can only work on a data matrix, not a data frame

# Convert log2 cpm filtered, normalized data frame into a matrix
log2.cpm.filtered.norm <- log2.cpm.filtered.norm.df %>%
  dplyr::select(!geneID) %>%
  data.matrix()
rownames(log2.cpm.filtered.norm) <- log2.cpm.filtered.norm.df$geneID


# Calculate distance matrix
# dist calculates distance between rows, so transpose data so that we get distance between samples.
# how similar are samples from each other
colnames(log2.cpm.filtered.norm)<-targets$group
distance <- dist(t(log2.cpm.filtered.norm), method = "maximum") #other distance methods are "euclidean", maximum", "manhattan", "canberra", "binary" or "minkowski"

# Calculate clusters to visualize differences. This is the hierarchical clustering.
# The methods here include: single (i.e. "friends-of-friends"), complete (i.e. complete linkage), and average (i.e. UPGMA). Here's a comparison of different types: https://en.wikipedia.org/wiki/UPGMA#Comparison_with_other_linkages
clusters <- hclust(distance, method = "complete") #other agglomeration methods are "ward.D", "ward.D2", "single", "complete", "average", "mcquitty", "median", or "centroid"
dend <- as.dendrogram(clusters) 

p1<-dend %>% 
  dendextend::set("branches_k_color", k = 5) %>% 
  dendextend::set("hang_leaves", c(0.1)) %>% 
  dendextend::set("labels_cex", c(0.5)) %>%
  dendextend::set("labels_colors", k = 5) %>% 
  dendextend::set("branches_lwd", c(0.7)) %>% 
  
  as.ggdend %>%
  ggplot (offset_labels = -0.5) +
  theme_dendro() +
  ylim(0, max(get_branches_heights(dend))) +
  labs(title = "Hierarchical Cluster Dendrogram ",
       subtitle = "filtered, TMM normalized",
       y = "Distance",
       x = "Life stage") +
  coord_fixed(1/2) +
  theme(axis.title.x = element_text(color = "black"),
        axis.title.y = element_text(angle = 90),
        axis.text.y = element_text(angle = 0),
        axis.line.y = element_line(color = "black"),
        axis.ticks.y = element_line(color = "black"),
        axis.ticks.length.y = unit(2, "mm"))
p1

# Principal component analysis (PCA) -------------
# this also works on a data matrix, not a data frame
pca.res <- prcomp(t(log2.cpm.filtered.norm), scale.=F, retx=T)
summary(pca.res) # Prints variance summary for all principal components.

#pca.res$rotation #$rotation shows you how much each gene influenced each PC (called 'scores')
#pca.res$x # 'x' shows you how much each sample influenced each PC (called 'loadings')
#note that these have a magnitude and a direction (this is the basis for making a PCA plot)
## This generates a screeplot: a standard way to view eigenvalues for each PCA. Shows the proportion of variance accounted for by each PC. Plotting only the first 10 dimensions.
p2<-fviz_eig(pca.res,
             barcolor = brewer.pal(8,"Pastel2")[8],
             barfill = brewer.pal(8,"Pastel2")[8],
             linecolor = "black",
             main = "Scree plot: proportion of variance accounted for by each principal component",
             ggtheme = theme_bw()) 
p2

pc.var<-pca.res$sdev^2 # sdev^2 captures these eigenvalues from the PCA result
pc.per<-round(pc.var/sum(pc.var)*100, 1) # we can then use these eigenvalues to calculate the percentage variance explained by each PC

# Visualize the PCA result ------------------
#lets first plot any two PCs against each other
#We know how much each sample contributes to each PC (loadings), so let's plot
pca.res.df <- as_tibble(pca.res$x)

# Plotting PC1 and PC2
p3<-ggplot(pca.res.df) +
  aes(x=PC1, y=PC2, label=groupLabels, 
      fill = groupLabels,
      color = groupLabels
  ) +
  geom_point(size=4, shape= 21, color = "black", alpha = 0.5) +
  #geom_label(color = "black", size = 2) +
  scale_fill_brewer(palette = "Set2") +
  scale_color_brewer(palette = "Set2", guide = FALSE) +
  #stat_ellipse() +
  xlab(paste0("PC1 (",pc.per[1],"%",")")) + 
  ylab(paste0("PC2 (",pc.per[2],"%",")")) +
  labs(title="Principal Components Analysis of S. stercoralis RNAseq Samples",
       sub = "Note: analysis is blind to life stage identity.",
       fill = "Life Stage") +
  scale_x_continuous(expand = c(.3, .3)) +
  scale_y_continuous(expand = c(.3, .3)) +
  coord_fixed() +
  theme_bw()
p3

# A guess at the identity of PC1
p4<-ggplot(pca.res.df) +
  aes(x=PC1, y=PC2, label=groupLabels,
      color = factor(targets$Maturity),
      fill = factor(targets$Maturity)
  ) +
  #geom_point(size=4) +
  #stat_ellipse() +
  geom_label(color = "black", size = 2, alpha = 0.7) +
  scale_fill_manual(values = brewer.pal(8,"Set2")) +
  scale_color_manual(values = brewer.pal(8,"Set2"), guide = FALSE) +
  xlab(paste0("PC1 (",pc.per[1],"%",")")) + 
  ylab(paste0("PC2 (",pc.per[2],"%",")")) +
  labs(subtitle="Potential PC1 identity: Adults vs Larvae",
       fill = "Maturity") +
  scale_x_continuous(expand = c(.3, .3)) +
  scale_y_continuous(expand = c(.3, .3)) +
  coord_fixed() +
  theme_bw()
p4

# A guess at the identity of PC2
p5<-ggplot(pca.res.df) +
  aes(x=PC1, y=PC2, label=groupLabels, 
      color = factor(targets$Infectious),
      fill = factor(targets$Infectious)
  ) +
  #geom_point(size=4) +
  #stat_ellipse() +
  geom_label(color = "black", size = 2, alpha = 0.7) +
  scale_fill_manual(values = brewer.pal(8,"Set2")[3:4]) +
  scale_color_manual(values = brewer.pal(8,"Set2")[3:4], guide = FALSE) +
  xlab(paste0("PC1 (",pc.per[1],"%",")")) + 
  ylab(paste0("PC2 (",pc.per[2],"%",")")) +
  labs(subtitle="Potential PC2 identity: Host-seeking/dwelling",
       fill = 'Infectivity Stage') +
  scale_x_continuous(expand = c(.3, .3)) +
  scale_y_continuous(expand = c(.3, .3)) +
  coord_fixed() +
  theme_bw()
p5

# Create a PCA 'small multiples' chart ----
pca.res.df <- pca.res$x[,1:6] %>% 
  as_tibble() %>%
  add_column(sample = sampleLabels,
             group = group,
             maturity = factor(targets$Maturity),
             infectious = factor(targets$Infectious))

pca.pivot <- pivot_longer(pca.res.df, # dataframe to be pivoted
                          cols = PC1:PC6, # column names to be stored as a SINGLE variable
                          names_to = "PC", # name of that new variable (column)
                          values_to = "loadings") # name of new variable (column) storing all the values (data)
PC1<-subset(pca.pivot, PC == "PC1")
PC2 <-subset(pca.pivot, PC == "PC2")
#PC3 <- subset(pca.pivot, PC == "PC3")
#PC4 <- subset(pca.pivot, PC == "PC4")

p6<-ggplot(pca.pivot) +
  aes(x=sample, y=loadings) + # you could iteratively 'paint' different covariates onto this plot using the 'fill' aes
  geom_bar(stat="identity", fill = brewer.pal(8,"Pastel2")[8]) +
  scale_fill_brewer(palette = "Set2") +
  facet_wrap(~PC) +
  geom_bar(data = PC1, stat = "identity", aes(fill = maturity)) +
  geom_bar(data = PC2, stat = "identity", aes(fill = infectious)) +
  labs(title="PCA 'small multiples' plot",
       fill = "Life Stage Groups",
       caption=paste0("produced on ", Sys.time())) +
  scale_x_discrete(limits = targets$sample, labels = targets$group) +
  theme_bw() +
  coord_flip()
p6

# Graph all the plots generated in this script ----
# Plot all the plots on a grid
# p7<- grid.arrange(p1,p2,p3,p4, p5,p6,ncol = 4,
#             layout_matrix = cbind(c(1,1,1,2,2,2),c(3,3,4,4,5,5),c(6,6,6,6,6,6), c(6,6,6,6,6,6)))
# ggsave("Strongyloides stercoralis RNAseq Multivariate Analysis.pdf", 
#        plot = p7, 
#        device = "pdf",
#        #height = 17,
#        width = 22,
#        path = './Outputs/'

Genes Contributing to PC Identity —

Differentially Expressed Genes —

# Introduction to this chunk ----
# This chunk uses a DGEList of filtered and normalized abundance data
# It will fit data to a linear model for responsively detecting differentially expressed genes (DEGs)
# 
# This chunk does a computationally intesnive thing: conductes pairwise differential expression analyses for every gene across every combintation of life stages.
# 
# Because we have access to biological replicates, we can use statistical tools for differential expression analysis
# Useful reading on differential expression: https://ucdavis-bioinformatics-training.github.io/2018-June-RNA-Seq-Workshop/thursday/DE.html

# Load packages ----
suppressPackageStartupMessages({
  library(tidyverse)
  library(limma) # differential gene expression using linear modeling
  library(edgeR)
  library(gt) 
  library(DT) 
  library(plotly) 
})

# Set up the design matrix ----
group <- factor(targets$group)
design <- model.matrix(~0 + group) # no intercept/blocking for matrix, comparisons across group
colnames(design) <- levels(group)

# NOTE: To handle a 'blocking' design' or a batch effect, use:
# design <- model.matrix(~block + treatment)

# Model mean-variance trend and fit linear model to data ----
# Use VOOM function from Limma package to model the mean-variance relationship
# produces a variance-stabilized DEGList, that include precision weights for each gene to try and control for heteroscedasity.
# transforms count data to log2-counts per million
# Outputs: E = normalized expression values on the log2 scale
v.DEGList.filtered.norm <- voom(counts = myDGEList.filtered.norm, 
                                design = design, plot = F)
colnames(v.DEGList.filtered.norm)<-targets$sample
colnames(v.DEGList.filtered.norm$E) <- paste(targets$group, targets$sample,sep = '-')

# Calculate average normalized log2-cmp across life stages ----
avg.v.filtered.norm.Log2CPM<-v.DEGList.filtered.norm$E %>%
  as_tibble(rownames = "geneID")%>%
  setNames(nm = c("geneID", targets$group)) %>%
  pivot_longer(cols = -geneID,
               names_to = "life_stage", 
               values_to = "log2CPM") %>%
  group_by(geneID, life_stage) %>%
  dplyr::summarize(life.stage.AVG = mean(log2CPM, na.rm = TRUE)) %>%
  pivot_wider(names_from = life_stage,
              names_prefix = "avg_",
              values_from = life.stage.AVG,) %>%
  ungroup() 

# Set Expression threshold values for plotting and saving DEGs ----
adj.P.thresh <- 0.05
lfc.thresh <- 1

# Fit a linear model to the data ----
fit <- lmFit(v.DEGList.filtered.norm, design)

# Generate comparison matrix for ALL the pairwise comparisons ----
comparisons <- crossing(targetStages = targets$group, 
                        contrastStages = targets$group) %>%
  dplyr::filter(!(targetStages == contrastStages))

paste.compare <- function(targetStage, contrastStage) {
  paste(targetStage, contrastStage, sep = "-")
}
contrasts <- mapply(paste.compare, comparisons$targetStages, comparisons$contrastStages, USE.NAMES = F)
contrast.matrix <- makeContrasts(contrasts = contrasts,
                                 levels=design)

# extract the linear model fit -----
fits <- contrasts.fit(fit, contrast.matrix)
# empirical bayes smoothing of gene-wise standard deviations provides increased power (see: https://www.degruyter.com/doi/10.2202/1544-6115.1027)
ebFit <- eBayes(fits)

# Pull out the DEGs that pass a specific threshold for all pairwise comparisons ----
results <- decideTests(ebFit, method="global", adjust.method="BH", p.value=adj.P.thresh, lfc=lfc.thresh)


# Function that identifies top DEG between a specific pairwise comparison ----
identify.DEGs <- function (selected.contrast, contrasts) {
  # TopTable to view DEGs -----
  # Look at the top differentially expressed genes
  # coef = which contrast to examine
  # adjust = how to adjust p values for multiple comparisons. 
  # Here using Benjamini-Hochberg false discovery rate adjusted p-value.
  # 
  # TopTable (from Limma) outputs a few different stats:
  # logFC, AveExpr, and P.Value should be self-explanatory, except for noting that AveExpr is the log2-expression across all groups
  # adj.P.Val is your adjusted P value, also known as an FDR (if BH method was used for multiple testing correction)
  # B statistic is the log-odds that that gene is differentially expressed. If B = 1.5, then log odds is e^1.5, where e is euler's constant (approx. 2.718).  So, the odds of differential expression os about 4.8 to 1 
  # t statistic is ratio of the logFC to the standard error (where the error has been moderated across all genes...because of Bayesian approach)
  # parse selected contrast into the coefficient
  coef <- grep(paste0("^",selected.contrast,"$"), contrasts)
  targetStage <- str_split(selected.contrast, "-")[[1]][1]
  contrastStage <- str_split(selected.contrast, "-")[[1]][2]
  
  myTopHits.df <- limma::topTable(ebFit, adjust ="BH", 
                                  coef=coef, number=40000, 
                                  sort.by="logFC") %>%
    as_tibble(rownames = "geneID") %>%
    dplyr::rename(tStatistic = t, LogOdds = B, BH.adj.P.Val = adj.P.Val) %>%
    dplyr::relocate(UniProtKB, Description, InterPro, GO_term, Ce_geneID, percent_homology, .after = LogOdds)
  
  # Volcano Plots ----
  vplot <- ggplot(myTopHits.df) +
    aes(y=-log10(BH.adj.P.Val), x=logFC, text = paste(geneID, "<br>",
                                                      "logFC:", round(logFC, digits = 2), "<br>",
                                                      "p-val:", format(BH.adj.P.Val, digits = 3, scientific = TRUE), "<br>",
                                                      "Description:", Description, "<br>", 
                                                      "InterPro:", InterPro,"<br>",
                                                      "Ce-Gene:", Ce_geneID, "<br>", 
                                                      "% homology:", percent_homology)) +
    geom_point(size=2) +
    geom_hline(yintercept = -log10(adj.P.thresh), linetype="longdash", colour="grey", size=1) + #dashed line at given p value, w/: -log10(p)
    geom_vline(xintercept = lfc.thresh, linetype="longdash", colour="#BE684D", size=1) + #logFC of 1 = 2 fold change
    geom_vline(xintercept = -lfc.thresh, linetype="longdash", colour="#2C467A", size=1) +
    labs(title="Volcano plot",
         subtitle = paste0("S. stercoralis ", selected.contrast),
         caption=paste0("produced on ", Sys.time())) +
    theme_bw()
  
  # Now make the volcano plot above interactive with plotly
  interactive.vplot <- ggplotly(vplot, tooltip = "text") %>%
    layout(title= list( text = paste0("Interactive Volcano plot",
                                      "<br>",
                                      "<sup>",
                                      "S. stercoralis ", selected.contrast,
                                      "</sup>")))
  
  # Generate variable containing expression data for your thresholded DEGs ----
  # Reminder that E (output from voom) = normalized expression values on the log2 scale
  diffGenes <- v.DEGList.filtered.norm$E[results[,coef] !=0,] # [,coef] specifies which pairwise comparison to pull out. This variable is necessary for generating heatmaps.
  diffGenes.df <- as_tibble(diffGenes, rownames = "geneID", .name_repair = "unique") %>% # convert DEGs to a dataframe
    left_join(., avg.v.filtered.norm.Log2CPM, by = "geneID")  # add averaged life stage expression information to the dataframe
  diffGenes.TopHits <- left_join(diffGenes.df, myTopHits.df, by = "geneID") # merge list of thresholded DEGs with information contained in TopTable
  diffGenes.TopHits$BH.adj.P.Val <- formatC(diffGenes.TopHits$BH.adj.P.Val, digits = 3, format = "E") # Set adjusted P value to scientific notation for cosmetic purposes
  
  # create interactive tables to display the thresholded DEGs, ranked ----
  if(any(grepl(targetStage, names(diffGenes.TopHits)))){
    DEG.datatable <- diffGenes.TopHits %>%
      dplyr::select(geneID, starts_with(paste0(targetStage,"-")), 
                    paste0("avg_",targetStage),
                    starts_with(paste0(contrastStage,"-")),
                    paste0("avg_", contrastStage),
                    logFC, BH.adj.P.Val:percent_homology)} else {
                      DEG.datatable <- diffGenes.TopHits %>%
                        dplyr::select(geneID, starts_with("avg"),logFC, BH.adj.P.Val:percent_homology)
                    }
  DEG.datatable <- DEG.datatable %>%
    DT::datatable(extensions = c('KeyTable', "FixedHeader"),
                  rownames = FALSE,
                  caption = htmltools::tags$caption(
                    style = 'caption-side: top; text-align: left;',
                    htmltools::tags$b('Differentially Expressed Genes in', htmltools::tags$em('S. stercoralis'), 
                                      targetStage, ' vs ', contrastStage),
                    htmltools::tags$br(),
                    "Threshold: p < ",
                    adj.P.thresh, "; log-fold change > ",
                    lfc.thresh,
                    htmltools::tags$br(),
                    'Values = log2 counts per million'),
                  options = list(keys = TRUE,
                                 autoWidth = TRUE,
                                 scrollX = TRUE,
                                 order = list(9, 'desc'),
                                 searchHighlight = TRUE, 
                                 pageLength = 10, 
                                 lengthMenu = c("10", "25", "50", "100")))
  
  if(any(grepl(targetStage, names(diffGenes.TopHits)))){
    DEG.datatable <- DEG.datatable %>%
      DT::formatRound(columns=c(2:9), digits=2) %>%
      DT::formatRound(columns = c(10,12), digits = 3)} else {
        DEG.datatable <- DEG.datatable %>%
          DT::formatRound(columns=c(2:8), digits=2) %>%
          DT::formatRound(columns = c(9,11), digits = 3)
      } 
  
  output <- list(myTopHits.df = myTopHits.df, 
                 diffGenes = diffGenes,
                 vplot = vplot, 
                 interactive.vplot = interactive.vplot, 
                 diffGenes.TopHits = diffGenes.TopHits,
                 DEG.datatable = DEG.datatable)
}

# Calling function on all pairwise comparisons -----
output.DEG.SsRNAseq <- sapply(contrasts, identify.DEGs, contrasts, simplify = F, USE.NAMES = T)
names(output.DEG.SsRNAseq) <-  paste(comparisons$targetStages, comparisons$contrastStages, sep = "-")

# Caching the call to identify.DEGs b/c it's computationally expensive.  ----
#if (!is.memoised(output.DEG.SsRNAseq)) {
# output.DEG.SsRNAseq <- memoise(output.DEG.SsRNAseq)}
# # Remove Cache - if I made a change to the underlying function
# forget(pairwise.Compare)

Across All Life Stages: DEGs —

diffGenes.TopHits.across %>%
    DT::datatable(extensions = c('KeyTable', "FixedHeader"),
                  rownames = FALSE,
                  caption = htmltools::tags$caption(
                    style = 'caption-side: top; text-align: left;',
                    htmltools::tags$b('Differentially Expressed Genes in', htmltools::tags$em('S. stercoralis')),
                    htmltools::tags$br(),
                    "Genes differentially expressed in any life stage",
                     htmltools::tags$br(),
                    "Threshold: p < ",
                    adj.P.thresh, "; log-fold change > ",
                    lfc.thresh,
                    htmltools::tags$br(),
                    'Values = log2 counts per million'),
                  options = list(keys = TRUE,
                                 autoWidth = TRUE,
                                 scrollX = TRUE,
                                 order = list(9, 'desc'),
                                 searchHighlight = TRUE, 
                                 pageLength = 10, 
                                 lengthMenu = c("10", "25", "50", "100"))) %>%
      DT::formatRound(columns=c(2:8), digits=2) %>%
      DT::formatRound(columns = c(9), digits = 3) 
It seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.htmlIt seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.html

Cluster DEGs into functional modules —

# Introduction to this chunk ----
# this chunk creates heatmaps from differentially expressed genes;
# it takes as input a list of genes that are differentially expressed in any life stage
# It selects modules of co-expressed genes based on pearson correlations

# Load packages -----
suppressPackageStartupMessages({
  library(tidyverse)
  library(limma)
  library(RColorBrewer)
  library(gplots)
})

# Choose a color pallette ----
myheatcolors <- rev(brewer.pal(name="RdBu", n=11))

# Cluster DEGs across stages ----
#begin by clustering the genes (rows) for a list of genes that are differentially expressed in at least one life stage
# use the 'cor' function and the pearson method for finding all pairwise correlations of genes
# '1-cor' converts this to a 0-2 scale for each of these correlations, which can then be used to calculate a distance matrix using 'as.dist'
clustRows <- hclust(as.dist(1-cor(t(diffGenes.across), method="pearson")), method="complete") 
# hierarchical clustering is a type of unsupervised clustering. 
# NOTE: this cluster may provide different results to one based on log2.cpm.filtered.norm data, likely b/c this version is specifcally focused on genes that are significantly different between conditions.
# Related methods include K-means, SOM, etc 
# unsupervised methods are blind to sample/group identity
# in contrast, supervised methods 'train' on a set of labeled data.  
# supervised clustering methods include random forest, and artificial neural networks

# cluster samples (columns)
clustColumns <- hclust(as.dist(1-cor(diffGenes.across, method="spearman")), method="complete") #cluster columns by spearman correlation
#note: use Spearman, instead of Pearson, for clustering samples because it gives equal weight to highly vs lowly expressed transcripts or genes

#Cut the resulting tree and create color vector for clusters.  
module.assign <- stats::cutree(clustRows, k=14) #The diffGenes info is based on a pairwise comparison between all 7 life stages. 

# assign a color to each module (makes it easy to identify and manipulate)
module.color <- rainbow(length(unique(module.assign)), start=0.1, end=0.9) 
module.color <- module.color[as.vector(module.assign)] 

# simplfy heatmap by averaging the biological replicates and display only one column per condition
colnames(diffGenes.across) <- targets$group
diffGenes.AVG <- avearrays(diffGenes.across)

# plot the hclust results as a heatmap, grouping the life stages together
diffGenes.heatmap.bygroup <- heatmap.2(diffGenes.AVG, 
                                       srtCol = 0, adjCol= c(0.5,0.5),
                                       Rowv=as.dendrogram(clustRows),
                                       key.title = NA,
                                       main = paste0("DEG Heatmap (by life stage): "),
                                       sub = paste0("Genes pass threshold in >= 1 pairwise comparison. Threshold: p < ",
                                                    adj.P.thresh, "; log-fold change > ",
                                                    lfc.thresh),
                                       RowSideColors=module.color,
                                       col=rev(myheatcolors), scale='row', labRow=NA,
                                       density.info="none", trace="none",  
                                       cexRow=1, cexCol=1) 


# Pairwise Clustering/Heatmaps
pairwise.Clustering <- function (targetStage,contrastStage){
  #Generate name of pairwise comparison
  x <- paste(targetStage, contrastStage, sep = "-")
  print(paste("Clustering ",x))
  subset.diffGenes <- output.DEG.SsRNAseq[[x]]$diffGenes
  colnames(subset.diffGenes) <- targets$group
  selected.cols <- grepl(paste0("^", targetStage, "$"), colnames(subset.diffGenes)) | 
    grepl(paste0("^", contrastStage, "$"), colnames(subset.diffGenes))
  subset.diffGenes <- subset.diffGenes[,selected.cols]
  
  subset.clustRows <- hclust(as.dist(1-cor(t(subset.diffGenes), method="pearson")), method="complete")
  subset.clustColumns <- hclust(as.dist(1-cor(subset.diffGenes, method="spearman")), method="complete") 
  subset.module.assign <- stats::cutree(subset.clustRows, k=2)
  subset.module.color <- rainbow(length(unique(subset.module.assign)), start=0.1, end=0.9) 
  subset.module.color <- subset.module.color[as.vector(subset.module.assign)]
  
  subset.diffGenes.heatmap.bygroup <- heatmap.2(subset.diffGenes, 
                                                srtCol = 0, adjCol= c(0.5,0.5),
                                                Rowv=as.dendrogram(subset.clustRows),
                                                Colv=as.dendrogram(subset.clustColumns),
                                                key.title = NA,
                                                main = paste0("DEG Heatmap (by life stage): ", x),
                                                sub = paste0("Threshold: p < ",
                                                             adj.P.thresh, "; log-fold change > ",
                                                             lfc.thresh),
                                                RowSideColors=subset.module.color,
                                                col=rev(myheatcolors), scale='row', labRow=NA,
                                                density.info="none", trace="none",
                                                cexRow=1, cexCol=1) 
  
  output <- list(subset.diffGenes.heatmap.bygroup = subset.diffGenes.heatmap.bygroup)
  
}
output.clustering.SsRNAseq <- mapply(pairwise.Clustering, comparisons$targetStages, comparisons$contrastStages, USE.NAMES = T, SIMPLIFY = FALSE)
[1] "Clustering  FLF-iL3"
[1] "Clustering  FLF-iL3a"

[1] "Clustering  FLF-PF"

[1] "Clustering  FLF-pfL1"

[1] "Clustering  FLF-ppL1"

[1] "Clustering  FLF-ppL3"

[1] "Clustering  iL3-FLF"

[1] "Clustering  iL3-iL3a"

[1] "Clustering  iL3-PF"

[1] "Clustering  iL3-pfL1"

[1] "Clustering  iL3-ppL1"

[1] "Clustering  iL3-ppL3"

[1] "Clustering  iL3a-FLF"

[1] "Clustering  iL3a-iL3"

[1] "Clustering  iL3a-PF"

[1] "Clustering  iL3a-pfL1"

[1] "Clustering  iL3a-ppL1"

[1] "Clustering  iL3a-ppL3"

[1] "Clustering  PF-FLF"

[1] "Clustering  PF-iL3"

[1] "Clustering  PF-iL3a"

[1] "Clustering  PF-pfL1"

[1] "Clustering  PF-ppL1"

[1] "Clustering  PF-ppL3"

[1] "Clustering  pfL1-FLF"

[1] "Clustering  pfL1-iL3"

[1] "Clustering  pfL1-iL3a"

[1] "Clustering  pfL1-PF"

[1] "Clustering  pfL1-ppL1"

[1] "Clustering  pfL1-ppL3"

[1] "Clustering  ppL1-FLF"

[1] "Clustering  ppL1-iL3"

[1] "Clustering  ppL1-iL3a"

[1] "Clustering  ppL1-PF"

[1] "Clustering  ppL1-pfL1"

[1] "Clustering  ppL1-ppL3"

[1] "Clustering  ppL3-FLF"

[1] "Clustering  ppL3-iL3"

[1] "Clustering  ppL3-iL3a"

[1] "Clustering  ppL3-PF"

[1] "Clustering  ppL3-pfL1"

[1] "Clustering  ppL3-ppL1"

names(output.clustering.SsRNAseq) <-  paste(comparisons$targetStages, comparisons$contrastStages, sep = "-")
# 

Output: Example Pairwise Comparisons: iL3 vs FLF —

output.DEG.SsRNAseq$`iL3-FLF`$interactive.vplot
output.DEG.SsRNAseq$`iL3-FLF`$DEG.datatable

Save Pairwise Comparison Data —

# Introduction to this chunk ----
# Saving the DEGList containing sample data and gene information. 
# Also saving all the pairwise comparison caluclations and plots.

# Save the filtered, normalized DGEList object ----
save(myDGEList.filtered.norm,
     file = "./Outputs/SsDGEList")

# Join and save the output lists of DEG Identification and Clustering functions
output.SsRNAseq <- Map(c, output.DEG.SsRNAseq, output.clustering.SsRNAseq)

output.SsRNAseq[["General"]] <- list(diffGenes.all.stages = diffGenes.across,
                                     diffGenes.heatmap.bygroup = diffGenes.heatmap.bygroup, 
                                     myscores.Top10 = myscores.Top10,
                                     PCA.plot = p3,
                                     PCA.small.multiples = p6
)

# Save the DEG datatables for all the pariwise comparisons, for working with a Shiny App
DEGs.only<- sapply(contrasts, function(x) {output.SsRNAseq[[x]]$DEG.datatable}, USE.NAMES = TRUE, simplify = F)
  
 save(DEGs.only,
      file = "./Outputs/SsLifeStageComparisons_subset")
LS0tCnRpdGxlOiBSZS1hbmFseXNpcyBvZiBTdHJvbmd5bG9pZGVzIHN0ZXJjb3JhbGlzIGJ1bGsgUk5Bc2VxIHZpYSBhbiBhbGlnbm1lbnQtZnJlZQogIGFuYWx5c2lzIHBpcGVsaW5lCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwotLS0KCiMgSW50cm9kdWN0aW9uIC0tLQpUaGUgaHVtYW4gcGFyYXNpdGljIG5lbWF0b2RlICpTdHJvbmd5bG9pZGVzIHN0ZXJjb3JhbGlzKiBpcyBlc3RpbWF0ZWQgdG8gaW5mZWN0IGFwcHJveGltYXRlbHkgNjEwIG1pbGxpb24gcGVvcGxlIChCdW9uZnJhdGUgKmV0IGFsKiAyMDIwKS4gU2ltaWxhciB0byBvdGhlciBpbnRlc3RpbmFsIHBhcmFzaXRpYyBuZW1hdG9kZXMsICpTLiBzdGVyY29yYWxpcyogaGFzIGEgY29tcGxleCBsaWZlIGN5Y2xlIGluIHdoaWNoIGRpc3RpbmN0IGRldmVsb3BtZW50YWwgc3RhZ2VzIG5hdmlnYXRlIHN0YXJrbHkgZGlmZmVyZW50IGVudmlyb25tZW50cyB2aWEgbGlmZS1zdGFnZSBzcGVjaWZpYyBiZWhhdmlvcmFsIHByZWZlcmVuY2VzICggW0Nhc3RlbGxldHRvICpldCBhbCogMjAxNF0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zcGF0aG9nZW5zL2FydGljbGU/aWQ9MTAuMTM3MS9qb3VybmFsLnBwYXQuMTAwNDMwNSksIFtCcnlhbnQgYW5kIEhhbGxlbSAyMDE4YV0oaHR0cHM6Ly93d3cuc2NpZW5jZWRpcmVjdC5jb20vc2NpZW5jZS9hcnRpY2xlL3BpaS9TMjIxMTMyMDcxODMwMTExOD92aWElM0RpaHViKSApLiBBY3Jvc3MgbmVtYXRvZGUgc3BlY2llcywgZXRob2xvZ2ljYWwgYW5kIGJlaGF2aW9yYWwgZGlmZmVyZW5jZXMgYXJlIG9mdGVuIHJlZmxlY3RlZCBpbiB0aGUgdGVtcG9yYWwgcmVndWxhdGlvbiBvZiBnZW5lIGV4cHJlc3Npb24uIEZvciAqUy4gc3RlcmNvcmFsaXMqLCBzZXZlcmFsIHN0dWRpZXMgaGF2ZSB1dGlsaXplZCBidWxrIFJOQSBzZXF1ZW5jaW5nIHRvIHByb2JlIHRoZSBnZW5vbWljIGJhc2lzIG9mIHBhcmFzaXRpc20gYnkgaWRlbnRpZnlpbmcgZ2VuZSBmYW1pbGllcyB0aGF0IGFyZSB1bmlxdWVseSB1cHJlZ3VsYXRlZCBpbiBwYXJhc2l0aWMgbGlmZSBzdGFnZXMgKCBbU3RvbHpmdXMgKmV0IGFsKiAyMDEyXShodHRwczovL2pvdXJuYWxzLnBsb3Mub3JnL3Bsb3NudGRzL2FydGljbGU/aWQ9MTAuMTM3MS9qb3VybmFsLnBudGQuMDAwMTg1NCksIFtIdW50ICpldCBhbCogMjAxNl0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9uZy4zNDk1KSwgW0h1bnQgKmV0IGFsKiAyMDE4XShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU5OC0wMTgtMjM1MTQteikpLiBUaGVzZSBlZmZvcnRzIGhpZ2hsaWdodCBhIGZlYXR1cmUgb2YgbW9kZXJuIGJpb2luZm9ybWF0aWNzIC0gdGhlIHNlY29uZGFyeSBhbmFseXNpcyBvZiBwdWJsaWNhbGx5IGF2YWxpYWJsZSBkYXRhc2V0cy4gSGVyZSwgYW4gb3JpZ2luYWwgZGF0YXNldCBmZWF0dXJpbmcgYnVsayBSTkEgc2VxdWVuY2luZyBvZiBzZXZlbiAqUy4gc3RlcmNvcmFsaXMqIGRldmVsb3BtZW50YWwgc3RhZ2VzIHdhcyBpbml0aWFsbHkgYWxpZ25lZCB0byBnZW5vbWljIGNvbnRpZ3MgKDYgRGVjZW1iZXIgMjAxMSBkcmFmdCwgW1N0b2x6ZnVzICpldCBhbCogMjAxMl0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zbnRkcy9hcnRpY2xlP2lkPTEwLjEzNzEvam91cm5hbC5wbnRkLjAwMDE4NTQpICkuIFN1YnNlcXVlbnRseSwgYSBzdWJzZXQgb2YgdGhpcyBkYXRhc2V0IHdhcyByZS1hbmFseXplZCBjb2luY2lkZW50IHdpdGggdGhlIHJlbGVhc2Ugb2YgYSBoaWdoLXF1YWxpdHkgZHJhZnQgYXNzZW1ibHkgKCBbSHVudCAqZXQgYWwqIDIwMTZdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvbmcuMzQ5NSkgKTsgdGhpcyByZS1hbmFseXNpcyBmb2N1c2VkIG9uIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRocmVlIGxpZmUgc3RhZ2VzOiBmcmVlLWxpdmluZyBhZHVsdHMgdGhhdCBuYXZpZ2F0ZSB0aGUgZW52aXJvbm1lbnQsIHBhcmFzaXRpYyBhZHVsdHMgbG9jYXRlZCB3aXRoaW4gdGhlIGhvc3QgaW50ZXN0aW5lLCBhbmQgaW5mZWN0aXZlIHRoaXJkLXN0YWdlIGxhcnZhZSB0aGF0IGFjdGl2ZWx5IGVuZ2FnZSBpbiBob3N0LXNlZWtpbmcgYmVoYXZpb3JzLiBBcyBnZW5vbWUgYXNzZW1ibHkgYW5kIFJOQSBzZXF1ZW5jaW5nIG9mIGFkZGl0aW9uYWwgcGFyYXNpdGljIG5lbWF0b2RlIHNwZWNpZXMgY29udGludWVzLCB0aGUgcHVibGljYWxseS1hdmFsaWFibGUgKlMuIHN0ZXJjb3JhbGlzKiBSTkFzZXEgZGF0YXNldCBjb250aW51ZXMgdG8gYmUgdXRpbGl6ZWQgZm9yIGNyb3NzLXNwZWNpZXMgYW5kIGNyb3NzLWxpZmUgc3RhZ2UgY29tcGFyaXNvbnMgKCBbSHVudCAqZXQgYWwqIDIwMTZdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvbmcuMzQ5NSksIFtIdW50ICpldCBhbCogMjAxOF0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1OTgtMDE4LTIzNTE0LXopICkuIEZ1cnRoZXJtb3JlLCBzZXZlcmFsIGhlbG1pbnRoIFJOQS1zZXEgZGF0YXNldHMsIGluY2x1ZGluZyAqUy4gc3RlcmNvcmFsaXMqIHdlcmUgcmVhbGlnbmVkIHRvIHRoZWlyIHJlZmVyZW5jZSBnZW5vbWVzIGFuZCBpbnRlZ3JhdGVkIGludG8gV29ybUJhc2UgUGFyYXNpdGUsIHdoZXJlIHRoZXkgYXJlIGFjY2Vzc2libGUgdG8gdGhlIGZpZWxkIGF0IGxhcmdlIGluIHRoZSBmb3JtIG9mIGdlbm9tZS1hbGlnbmVkIFJOQS1zZXEgZXhwcmVzc2lvbiB0cmFja3MgKCBbSG93ZSAqZXQgYWwqIDIwMTddKGh0dHBzOi8vd3d3LnNjaWVuY2VkaXJlY3QuY29tL3NjaWVuY2UvYXJ0aWNsZS9waWkvUzAxNjY2ODUxMTYzMDE2MDg/dmlhJTNEaWh1YikgKS4KCkFzIHJlc2VhcmNoIGludG8gdGhlIGdlbm9taWMgYmFzaXMgb2YgcGFyYXNpdGlzbSBpbiBoZWxtaW50aHMgY29udGludWVzLCBhY2Nlc3MgdG8gcXVhbnRpdGF0aXZlIGdlbmUgZXhwcmVzc2lvbiBsZXZlbHMgd2lsbCBncmVhdGx5IGVuaGFuY2Ugc3R1ZGllcyBpbnRvIHRoZSBmdW5jdGlvbmFsIHJvbGUgb2Ygc3BlY2lmaWMgZ2VuZXMgYW5kIGdlbmUgZmFtaWxpZXMuIERpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gcmVzdWx0cyBoYXZlIGJlZW4gcHVibGlzaGVkIGFzIHN1cHBsZW1lbnRhbCBkYXRhICggW0h1bnQgKmV0IGFsKiAyMDE2XShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25nLjM0OTUpICksIGhvd2V2ZXIgdGhlc2UgYW5hbHlzZXMgb25seSBpbmNsdWRlZCBwYWlyd2lzZSBjb21wYXJpc29ucyBiZXR3ZWVuIHRocmVlIGxpZmUgc3RhZ2VzLiBRdWFudGl0YXRpdmUgY29tcGFyaXNvbnMgdGhhdCB1dGlsaXplIHRoZSBmdWxsIHNldmVuIGF2YWxpYWJsZSBsaWZlIHN0YWdlcyBoYXZlIG5vdCB5ZXQgYmVlbiBwdWJsaXNoZWQuIAoKVGhlIGludHJvZHVjdGlvbiBvZiBhbGlnbm1lbnQtZnJlZSByZWFkIG1hcHBpbmcgdG9vbHMgc3VjaCBhcyBLYWxsaXN0byBoYXMgc2lnbmlmaWNhbnRseSBsb3dlcmVkIHRoZSBjb21wdXRhdGlvbmFsIGJhcnJpZXIgZm9yIHJlLWFuYWx5c2lzIG9mIHB1YmxpY2FsbHkgYXZhbGlhYmxlIFJOQXNlcSBkYXRhc2V0cy4gSGVyZSwgSSB0YWtlIGFkdmFudGFnZSBvZiB0aGVzZSB0b29scyB0byByZS1hbmFseXplIHRoZSAqUy4gc3Rlcm9jb3JhbGlzKiBidWxrIFJOQS1zZXEgZGF0YSwgZXh0ZW5kaW5nIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzZXMgYWNyb3NzIGFsbCBhdmFsaWFibGUgZGV2ZWxvcG1lbnRhbCBzdGFnZXMuIEthbGxpc3RvIGFuZCBjdXN0b20gUiBzY3JpcHRzIHdlcmUgdXNlZCB0byBwZXJmb3JtIHVsdHJhLWZhc3QgcmVhZCBtYXBwaW5nIG9mIHJhdyByZWFkcyB0byB0aGUgKlMuIHN0ZXJjb2FsaXMqIHJlZmVyZW5jZSB0cmFuc2NyaXB0b21lIChQUkpFQjUyOC5XQlBTMTQubVJOQV90cmFuc2NyaXB0cywgZG93bmxvYWRlZCBmcm9tIFtXb3JtQmFzZSBQYXJhc2l0ZV0oaHR0cHM6Ly9wYXJhc2l0ZS53b3JtYmFzZS5vcmcvU3Ryb25neWxvaWRlc19zdGVyY29yYWxpc19wcmplYjUyOC9JbmZvL0luZGV4Lykgb24gMTYgSnVuZSAyMDIwKTsgcmF3IGNvdW50IGRhdGEgd2FzIHNhdmVkIGFzIGEgZGlnaXRpYWwgZ2VuZSBleHByZXNzaW9uIGxpc3QuIFJhdyByZWFkcyB3ZXJlIHF1YW50aWZpZWQgYXMgY291bnRzIHBlciBtaWxsaW9uIHVzaW5nIHRoZSBFZGdlUiBwYWNrYWdlLCB0aGVuIGZpbHRlcmVkIHRvIHJlbW92ZSB0cm5zY3JpcHRzIHdpdGggbG93IGNvdW50cyAobGVzcyB0aGFuIDEgY291bnQtcGVyLW1pbGxpb24gaW4gYXQgbGVhc3QgMyBzYW1wbGVzKSwgYW5kIG5vcm1hbGl6ZWQgdXNpbmcgdGhlIHRyaW1tZWQgbWVhbiBvZiBNLXZhbHVlcyBtZXRob2QgKFRNTSwgW1JvYmluc29uIGFuZCBPc2hsYWNrXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9nYi0yMDEwLTExLTMtcjI1KSApIHRvIHBlcm1pdCBiZXR3ZWVuLXNhbXBsZXMgY29tcGFyaXNvbnMuIFByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgYXBwbGllZCB0byB0aGlzIGRhdGEgaWRlbnRpZmllZCB0d28gZGV2ZWxvcG1lbnRhbCBjbHVzdGVycyB0aGF0IGFjY291bnQgZm9yIHh4JSBhbmQgeHglIG9mIGV4cHJlc3Npb24gdmFyaWFiaWxpdHkgYmV0d2VlbiAqUy4gc3RlcmNvcmFsaXMqIGRldmVsb3BtZW50YWwgc3RhZ2VzLiBUaGUgbGltbWEgcGFja2FnZSAoIFtSaXRjaGllICpldCBhbCogMjAxNV0oaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8yNTYwNTc5Mi8pLCBbUGhpcHNvbiAqZXQgYWwqIDIwMTZdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzUzNzM4MTIvKSkgd2FzIHVzZWQgdG8gY29uZHVjdCBwYWlyd2lzZSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFuYWx5c2VzIGJldHdlZW4gbGlmZSBzdGFnZXMgb3IgZGV2ZWxvcG1lbnRhbCBjbHVzdGVycy4gVGhlIG1lYW4tdmFpcmFuY2UgcmVsYXRpb25zaGlwIHdhcyBtb2RlbGVkIHVzaW5nIGEgcHJlY2lzaW9uIHdpZ2h0cyBhcHByb2FjaCBbTGF3ICpldCBhbCogMjAxNF0oaHR0cHM6Ly9nZW5vbWViaW9sb2d5LmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvZ2ItMjAxNC0xNS0yLXIyOSkuCgpbSW4gcHJvZ3Jlc3M6IEZpbmFsbHksIGEgU2hpbnkgV2ViIEFwcCBpbnRlcmZhY2Ugd2FzIGNyZWF0ZWQgdG8gZW5hYmxlIG5vbi10ZWNobmljYWwgdXNlcnMgdG8gYWNjZXNzIHRoZXNlIHF1YW50aXRhdGl2ZSBhbmFseXNlcy4gVGhlIHNpdGUgaW5jbHVkZXMgdHdvIG1vZGVzIGZvciBkYXRhIGV4cGxvcmF0aW9uLiBGaXJzdCwgYSBHZW5lIFNlYXJjaCBtb2RlIGFsbG93cyB1c2VycyB0byBkb3dubG9hZCBjb3VudHMgYW5kIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHJlc3VsdHMgZm9yIHNwZWNpZmljIGdlbmVzIG9mIGludGVyZXN0LiBTZWNvbmQsIGEgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQnJvd3NlciBtb2RlIGVuYWJsZXMgdXNlcnMgdG8gbWFrZSBwYWlyLXdpc2UgY29tcGFyaXNvbnMgYmV0d2VlbiBpbmRpdmlkdWFsIGRldmVsb3BtZW50IHN0YWdlcyBvciBQQ0EtaWRlbnRpZmllZCBjbHVzdGVycywgdGhlbiB2aXN1YWxpemUgYW5kIGRvd25sb2FkIGxpc3RzIG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyB0aGF0IHBhc3MgdXNlci1zcGVjaWZpZWQgdGhyZXNob2xkcyBmb3IgbWFnbml0dWRlIGFuZCBzaWduaWZpY2FuY2UgKGxvZ0ZDIGFuZCBCZW5qYW1pbmktSG9jaGJlcmcgYWRqdXN0ZWQgcC12YWx1ZXMsIHJlc3BlY3RpdmVseSkuXQoKIyBLYWxsaXN0byByZWFkIG1hcHBpbmcgLS0tCmBgYHtiYXNoIEthbGxpc3RvTWFwcGluZywgZXZhbD1GQUxTRX0KIyBUaGlzIHNjcmlwdCBjaGVja3MgdGhlIHF1YWxpdGl5IG9mIHRoZSBmYXN0cSBmaWxlcyBhbmQgcGVyZm9ybXMgYW4gYWxpZ25tZW50IHRvIHRoZSBTdHJvbmd5bG9pZGVzIHN0ZXJjb3JhbGlzIGNETkEgdHJhbnNjcmlwdG9tZSByZWZlcmVuY2Ugd2l0aCBLYWxsaXN0by4KIyBUbyBydW4gdGhpcyAnc2hlbGwgc2NyaXB0JyB5b3Ugd2lsbCBuZWVkIHRvIG9wZW4geW91ciB0ZXJtaW5hbCBhbmQgbmF2aWdhdGUgdG8gdGhlIGRpcmVjdG9yeSB3aGVyZSB0aGlzIHNjcmlwdCByZXNpZGVzIG9uIHlvdXIgY29tcHV0ZXIuCiMgVGhpcyBzaG91bGQgYmUgdGhlIHNhbWUgZGlyZWN0b3J5IHdoZXJlIHlvdSBmYXN0cSBmaWxlcyBhbmQgcmVmZXJlbmNlIGZhc3RhIGZpbGUgYXJlIGZvdW5kLgojIENoYW5nZSBwZXJtaXNzaW9ucyBvbiB5b3VyIGNvbXB1dGVyIHNvIHRoYXQgeW91IGNhbiBydW4gYSBzaGVsbCBzY3JpcHQgYnkgdHlwaW5nOiAnY2htb2QgdSt4IHJlYWRNYXBwaW5nLnNoJyAod2l0aG91dCB0aGUgcXVvdGVzKSBhdCB0aGUgdGVybWluYWwgcHJvbXB0IAojIFRoZW4gdHlwZSAnLi9yZWFkTWFwcGluZy5zaCcgKHdpdGhvdXQgdGhlIHF1b3RlcykgYXQgdGhlIHByb21wdC4gIAojIFRoaXMgd2lsbCBiZWdpbiB0aGUgcHJvY2VzcyBvZiBydW5uaW5nIGVhY2ggbGluZSBvZiBjb2RlIGluIHRoZSBzaGVsbCBzY3JpcHQuCgojIGZpcnN0IHVzZSBmYXN0cWMgdG8gY2hlY2sgdGhlIHF1YWxpdHkgb2Ygb3VyIGZhc3RxIGZpbGVzOgpmYXN0cWMgKi5neiAtdCAxNAoKIyBuZXh0LCB3ZSB3YW50IHRvIGJ1aWxkIGFuIGluZGV4IGZyb20gb3VyIHJlZmVyZW5jZSBmYXN0YSBmaWxlIAojIEkgZ290IG15IHJlZmVyZW5jZSBTdHJvbmd5bG9pZGVzIHN0ZXJvY3JhbGlzIHRyYW5zY3JpcHRzIGZyb20gV29ybUJhc2UgUGFyYXNpdGUKa2FsbGlzdG8gaW5kZXggLWkgc3Ryb25neWxvaWRlc19zdGVyY29yYWxpcy5QUkpFQjUyOC5XQlBTMTQubVJOQV90cmFuc2NyaXB0cy5pbmRleCBzdHJvbmd5bG9pZGVzX3N0ZXJjb3JhbGlzLlBSSkVCNTI4LldCUFMxNC5tUk5BX3RyYW5zY3JpcHRzLmZhCgojIG5vdyBtYXAgcmVhZHMgdG8gdGhlIGluZGV4ZWQgcmVmZXJlbmNlIGhvc3QgdHJhbnNjcmlwdG9tZQojIHVzZSBhcyBtYW55ICd0aHJlYWRzJyBhcyB5b3VyIG1hY2hpbmUgd2lsbCBhbGxvdyBpbiBvcmRlciB0byBzcGVlZCB1cCB0aGUgcmVhZCBtYXBwaW5nIHByb2Nlc3MuCiMgbm90ZSB0aGF0IHdlJ3JlIGFsc28gaW5jbHVkaW5nIHRoZSAnJj4nIGF0IHRoZSBlbmQgb2YgZWFjaCBsaW5lCiMgdGhpcyB0YWtlcyB0aGUgaW5mb3JtYXRpb24gdGhhdCB3b3VsZCd2ZSBiZWVuIHByaW50ZWQgdG8gb3VyIHRlcm1pbmFsLCBhbmQgb3V0cHV0cyB0aGlzIGluIGEgbG9nIGZpbGUgdGhhdCBpcyBzYXZlZCBpbiAvZGF0YS9jb3Vyc2VfZGF0YQoKIyBGcmVlLUxpdmluZyBGZW1hbGVzCmthbGxpc3RvIHF1YW50IC1pIHN0cm9uZ3lsb2lkZXNfc3RlcmNvcmFsaXMuUFJKRUI1MjguV0JQUzE0Lm1STkFfdHJhbnNjcmlwdHMuaW5kZXggLW8gRVJSMTQ2OTQxIC10IDE0IEVSUjE0Njk0MV8xLmZhc3RxLmd6IEVSUjE0Njk0MV8yLmZhc3RxLmd6Jj4gRVJSMTQ2OTQxLmxvZwprYWxsaXN0byBxdWFudCAtaSBzdHJvbmd5bG9pZGVzX3N0ZXJjb3JhbGlzLlBSSkVCNTI4LldCUFMxNC5tUk5BX3RyYW5zY3JpcHRzLmluZGV4IC1vIEVSUjE0Njk0MiAtdCAxNCBFUlIxNDY5NDJfMS5mYXN0cS5neiBFUlIxNDY5NDJfMi5mYXN0cS5neiY+IEVSUjE0Njk0Mi5sb2cKa2FsbGlzdG8gcXVhbnQgLWkgc3Ryb25neWxvaWRlc19zdGVyY29yYWxpcy5QUkpFQjUyOC5XQlBTMTQubVJOQV90cmFuc2NyaXB0cy5pbmRleCAtbyBFUlIxNDY5NDMgLXQgMTQgRVJSMTQ2OTQzXzEuZmFzdHEuZ3ogRVJSMTQ2OTQzXzIuZmFzdHEuZ3omPiBFUlIxNDY5NDMubG9nCgojIEwzaSsgKEFjdGl2YXRlZCBpTDNzKQprYWxsaXN0byBxdWFudCAtaSBzdHJvbmd5bG9pZGVzX3N0ZXJjb3JhbGlzLlBSSkVCNTI4LldCUFMxNC5tUk5BX3RyYW5zY3JpcHRzLmluZGV4IC1vIEVSUjE0Njk0NCAtdCAxNCBFUlIxNDY5NDRfMS5mYXN0cS5neiBFUlIxNDY5NDRfMi5mYXN0cS5neiY+IEVSUjE0Njk0NC5sb2cKa2FsbGlzdG8gcXVhbnQgLWkgc3Ryb25neWxvaWRlc19zdGVyY29yYWxpcy5QUkpFQjUyOC5XQlBTMTQubVJOQV90cmFuc2NyaXB0cy5pbmRleCAtbyBFUlIxNDY5NDUgLXQgMTQgRVJSMTQ2OTQ1XzEuZmFzdHEuZ3ogRVJSMTQ2OTQ1XzIuZmFzdHEuZ3omPiBFUlIxNDY5NDUubG9nCmthbGxpc3RvIHF1YW50IC1pIHN0cm9uZ3lsb2lkZXNfc3RlcmNvcmFsaXMuUFJKRUI1MjguV0JQUzE0Lm1STkFfdHJhbnNjcmlwdHMuaW5kZXggLW8gRVJSMTQ2OTQ2IC10IDE0IEVSUjE0Njk0Nl8xLmZhc3RxLmd6IEVSUjE0Njk0Nl8yLmZhc3RxLmd6Jj4gRVJSMTQ2OTQ2LmxvZwoKIyBMM2kKa2FsbGlzdG8gcXVhbnQgLWkgc3Ryb25neWxvaWRlc19zdGVyY29yYWxpcy5QUkpFQjUyOC5XQlBTMTQubVJOQV90cmFuc2NyaXB0cy5pbmRleCAtbyBFUlIxNDY5NDcgLXQgMTQgRVJSMTQ2OTQ3XzEuZmFzdHEuZ3ogRVJSMTQ2OTQ3XzIuZmFzdHEuZ3omPiBFUlIxNDY5NDcubG9nCmthbGxpc3RvIHF1YW50IC1pIHN0cm9uZ3lsb2lkZXNfc3RlcmNvcmFsaXMuUFJKRUI1MjguV0JQUzE0Lm1STkFfdHJhbnNjcmlwdHMuaW5kZXggLW8gRVJSMTQ2OTQ4IC10IDE0IEVSUjE0Njk0OF8xLmZhc3RxLmd6IEVSUjE0Njk0OF8yLmZhc3RxLmd6Jj4gRVJSMTQ2OTQ4LmxvZwprYWxsaXN0byBxdWFudCAtaSBzdHJvbmd5bG9pZGVzX3N0ZXJjb3JhbGlzLlBSSkVCNTI4LldCUFMxNC5tUk5BX3RyYW5zY3JpcHRzLmluZGV4IC1vIEVSUjE0Njk0OSAtdCAxNCBFUlIxNDY5NDlfMS5mYXN0cS5neiBFUlIxNDY5NDlfMi5mYXN0cS5neiY+IEVSUjE0Njk0OS5sb2cKCiMgUG9zdC1GcmVlLUxpdmluZyBMMXMKa2FsbGlzdG8gcXVhbnQgLWkgc3Ryb25neWxvaWRlc19zdGVyY29yYWxpcy5QUkpFQjUyOC5XQlBTMTQubVJOQV90cmFuc2NyaXB0cy5pbmRleCAtbyBFUlIxNDY5NTAgLXQgMTQgRVJSMTQ2OTUwXzEuZmFzdHEuZ3ogRVJSMTQ2OTUwXzIuZmFzdHEuZ3omPiBFUlIxNDY5NTAubG9nCmthbGxpc3RvIHF1YW50IC1pIHN0cm9uZ3lsb2lkZXNfc3RlcmNvcmFsaXMuUFJKRUI1MjguV0JQUzE0Lm1STkFfdHJhbnNjcmlwdHMuaW5kZXggLW8gRVJSMTQ2OTUxIC10IDE0IEVSUjE0Njk1MV8xLmZhc3RxLmd6IEVSUjE0Njk1MV8yLmZhc3RxLmd6Jj4gRVJSMTQ2OTUxLmxvZwprYWxsaXN0byBxdWFudCAtaSBzdHJvbmd5bG9pZGVzX3N0ZXJjb3JhbGlzLlBSSkVCNTI4LldCUFMxNC5tUk5BX3RyYW5zY3JpcHRzLmluZGV4IC1vIEVSUjE0Njk1MiAtdCAxNCBFUlIxNDY5NTJfMS5mYXN0cS5neiBFUlIxNDY5NTJfMi5mYXN0cS5neiY+IEVSUjE0Njk1Mi5sb2cKCiMgUG9zdC1QYXJhc2l0aWMgTDFzCmthbGxpc3RvIHF1YW50IC1pIHN0cm9uZ3lsb2lkZXNfc3RlcmNvcmFsaXMuUFJKRUI1MjguV0JQUzE0Lm1STkFfdHJhbnNjcmlwdHMuaW5kZXggLW8gRVJSMTQ2OTUzIC10IDE0IEVSUjE0Njk1M18xLmZhc3RxLmd6IEVSUjE0Njk1M18yLmZhc3RxLmd6Jj4gRVJSMTQ2OTUzLmxvZwprYWxsaXN0byBxdWFudCAtaSBzdHJvbmd5bG9pZGVzX3N0ZXJjb3JhbGlzLlBSSkVCNTI4LldCUFMxNC5tUk5BX3RyYW5zY3JpcHRzLmluZGV4IC1vIEVSUjE0Njk1NCAtdCAxNCBFUlIxNDY5NTRfMS5mYXN0cS5neiBFUlIxNDY5NTRfMi5mYXN0cS5neiY+IEVSUjE0Njk1NC5sb2cKa2FsbGlzdG8gcXVhbnQgLWkgc3Ryb25neWxvaWRlc19zdGVyY29yYWxpcy5QUkpFQjUyOC5XQlBTMTQubVJOQV90cmFuc2NyaXB0cy5pbmRleCAtbyBFUlIxNDY5NTUgLXQgMTQgRVJSMTQ2OTU1XzEuZmFzdHEuZ3ogRVJSMTQ2OTU1XzIuZmFzdHEuZ3omPiBFUlIxNDY5NTUubG9nCgojIFBvc3QtUGFyYXNpdGljIEwxcwprYWxsaXN0byBxdWFudCAtaSBzdHJvbmd5bG9pZGVzX3N0ZXJjb3JhbGlzLlBSSkVCNTI4LldCUFMxNC5tUk5BX3RyYW5zY3JpcHRzLmluZGV4IC1vIEVSUjE0Njk1NiAtdCAxNCBFUlIxNDY5NTZfMS5mYXN0cS5neiBFUlIxNDY5NTZfMi5mYXN0cS5neiY+IEVSUjE0Njk1Ni5sb2cKa2FsbGlzdG8gcXVhbnQgLWkgc3Ryb25neWxvaWRlc19zdGVyY29yYWxpcy5QUkpFQjUyOC5XQlBTMTQubVJOQV90cmFuc2NyaXB0cy5pbmRleCAtbyBFUlIxNDY5NTcgLXQgMTQgRVJSMTQ2OTU3XzEuZmFzdHEuZ3ogRVJSMTQ2OTU3XzIuZmFzdHEuZ3omPiBFUlIxNDY5NTcubG9nCmthbGxpc3RvIHF1YW50IC1pIHN0cm9uZ3lsb2lkZXNfc3RlcmNvcmFsaXMuUFJKRUI1MjguV0JQUzE0Lm1STkFfdHJhbnNjcmlwdHMuaW5kZXggLW8gRVJSMTQ2OTU4IC10IDE0IEVSUjE0Njk1OF8xLmZhc3RxLmd6IEVSUjE0Njk1OF8yLmZhc3RxLmd6Jj4gRVJSMTQ2OTU4LmxvZwoKIyBQYXJhc2l0aWMgRmVtYWxlcwprYWxsaXN0byBxdWFudCAtaSBzdHJvbmd5bG9pZGVzX3N0ZXJjb3JhbGlzLlBSSkVCNTI4LldCUFMxNC5tUk5BX3RyYW5zY3JpcHRzLmluZGV4IC1vIEVSUjE0Njk1OSAtdCAxNCBFUlIxNDY5NTlfMS5mYXN0cS5neiBFUlIxNDY5NTlfMi5mYXN0cS5neiY+IEVSUjE0Njk1OS5sb2cKa2FsbGlzdG8gcXVhbnQgLWkgc3Ryb25neWxvaWRlc19zdGVyY29yYWxpcy5QUkpFQjUyOC5XQlBTMTQubVJOQV90cmFuc2NyaXB0cy5pbmRleCAtbyBFUlIxNDY5NjAgLXQgMTQgRVJSMTQ2OTYwXzEuZmFzdHEuZ3ogRVJSMTQ2OTYwXzIuZmFzdHEuZ3omPiBFUlIxNDY5NjAubG9nCmthbGxpc3RvIHF1YW50IC1pIHN0cm9uZ3lsb2lkZXNfc3RlcmNvcmFsaXMuUFJKRUI1MjguV0JQUzE0Lm1STkFfdHJhbnNjcmlwdHMuaW5kZXggLW8gRVJSMTQ2OTYxIC10IDE0IEVSUjE0Njk2MV8xLmZhc3RxLmd6IEVSUjE0Njk2MV8yLmZhc3RxLmd6Jj4gRVJSMTQ2OTYxLmxvZwoKCgojIHN1bW1hcml6ZSBmYXN0cWMgYW5kIGthbGxpc3RvIG1hcHBpbmcgcmVzdWx0cyBpbiBhIHNpbmdsZSBzdW1tYXJ5IGh0bWwgdXNpbmcgTXVsdGlRQwptdWx0aXFjIC1kIC4gCgplY2hvICJGaW5pc2hlZCIKYGBgCgojIEltcG9ydCBLYWxsaXN0byByZWFkcyBpbnRvIFIgLS0tCmBgYHtyIHR4SW1wb3J0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGxvYWQgcGFja2FnZXMgLS0tLQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkodGlkeXZlcnNlKSAKICBsaWJyYXJ5KHR4aW1wb3J0KQogIGxpYnJhcnkoZW5zZW1ibGRiKQogIGxpYnJhcnkoYmlvbWFSdCkKICBsaWJyYXJ5KG1hZ3JpdHRyKQp9KQojIHJlYWQgaW4gdGhlIHN0dWR5IGRlc2lnbiAtLS0tCnRhcmdldHMgPC0gcmVhZF90c3YoIi4vRGF0YS9QUkpFQjMxMTZfc3R1ZHlfZGVzaWduLnR4dCIsCiAgICAgICAgICAgICAgICAgICAgbmEgPSBjKCIiLCAiTkEiLCAibmEiKSkKIyBjcmVhdGUgZmlsZSBwYXRocyB0byB0aGUgYWJ1bmRhbmNlIGZpbGVzIGdlbmVyYXRlZCBieSBLYWxsaXN0byB1c2luZyB0aGUgJ2ZpbGUucGF0aCcgZnVuY3Rpb24KcGF0aCA8LSBmaWxlLnBhdGgoIi4vRGF0YSIsdGFyZ2V0cyRzYW1wbGUsICJhYnVuZGFuY2UudHN2IikKIyBjaGVjayB0byBtYWtlIHN1cmUgdGhpcyBwYXRoIGlzIGNvcnJlY3QgYnkgc2VlaW5nIGlmIHRoZSBmaWxlcyBleGlzdAojYWxsKGZpbGUuZXhpc3RzKHBhdGgpKSAKCiMgZ2V0IGFubm90YXRpb25zIHVzaW5nIG9yZ2FuaXNtLXNwZWNpZmljIHBhY2thZ2UgLS0tLQpUeC5TcyA8LSBnZXRCTShhdHRyaWJ1dGVzPWMoJ3dicHNfdHJhbnNjcmlwdF9pZCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnd2Jwc19nZW5lX2lkJyksCiAgICAgICAgICAgICAgICMgZ3JhYiB0aGUgZW5zZW1ibCBhbm5vdGF0aW9ucyBmb3IgV29ybWJhc2UgUGFyYXNpdGUgZ2VuZXMKICAgICAgICAgICAgICAgbWFydCA9IHVzZU1hcnQoYmlvbWFydD0icGFyYXNpdGVfbWFydCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhc2V0ID0gIndicHNfZ2VuZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3N0PSJodHRwczovL3BhcmFzaXRlLndvcm1iYXNlLm9yZyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3J0ID0gNDQzKSwKICAgICAgICAgICAgICAgZmlsdGVycyA9IGMoJ3NwZWNpZXNfaWRfMTAxMCcpLAogICAgICAgICAgICAgICB2YWx1ZSA9IGxpc3QoJ3N0c3RlcnByamViNTI4JykpICU+JQogIGFzX3RpYmJsZSgpICU+JQogICN3ZSBuZWVkIHRvIHJlbmFtZSB0aGUgY29sdW1ucyByZXRyZWl2ZWQgZnJvbSBiaW9tYXJ0CiAgZHBseXI6OnJlbmFtZSh0YXJnZXRfaWQgPSB3YnBzX3RyYW5zY3JpcHRfaWQsCiAgICAgICAgICAgICAgICBnZW5lX25hbWUgPSB3YnBzX2dlbmVfaWQpIAoKCiMgaW1wb3J0IEthbGxpc3RvIHRyYW5zY3JpcHQgY291bnRzIGludG8gUiB1c2luZyBUeGltcG9ydCAtLS0tCiMgY29weSB0aGUgYWJ1bmRhbmNlIGZpbGVzIHRvIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBhbmQgcmVuYW1lIHNvIHRoYXQgZWFjaCBzYW1wbGUgaGFzIGEgdW5pcXVlIG5hbWUKVHhpX2dlbmUgPC0gdHhpbXBvcnQocGF0aCwgCiAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAia2FsbGlzdG8iLCAKICAgICAgICAgICAgICAgICAgICAgdHgyZ2VuZSA9IFR4LlNzWywxOjJdLCAKICAgICAgICAgICAgICAgICAgICAgdHhPdXQgPSBGQUxTRSwgI0hvdyBkb2VzIHRoZSByZXN1bHQgY2hhbmdlIGlmIHRoaXMgPUZBTFNFIHZzID1UUlVFPwogICAgICAgICAgICAgICAgICAgICBjb3VudHNGcm9tQWJ1bmRhbmNlID0gImxlbmd0aFNjYWxlZFRQTSIsCiAgICAgICAgICAgICAgICAgICAgIGlnbm9yZVR4VmVyc2lvbiA9IEZBTFNFKQpgYGAKIyBBbm5vdGF0ZSBHZW5lcyB3aXRoIEMuIGVsZWdhbnMgaG9tb2xvZ3kgYW5kIGdlbmUgRGVzY3JpcHRpb24gVGVybXMgLS0tCmBgYHtyIGdlbmVBbm5vdGF0aW9uLCBtZXNzYWdlPUZBTFNFfQojIEludHJvZHVjdGlvbiB0byB0aGlzIGNodW5rIC0tLS0tLS0tLS0tCiMgVGhpcyBjaHVuayBpbXBvcnRzIGdlbmUgYW5ub3RhdGlvbiBpbmZvcm1hdGlvbiBmb3IgUy4gc3RlcmNvcmFsaXMgZ2VuZXMsIGluY2x1ZGluZzoKIyBDLiBlbGVnYW5zIGhvbW9sb2dzLCBwZXJjZW50IGhvbW9sb2d5LCBhbmQgYW55IEludGVycHJvdCBvciBnZW5lcmFsIERlc2NyaXB0aW9uIGluZm9ybWF0aW9uIHVzaW5nIGJpb21hcnQuCiMgSXQgd2lsbCBzYXZlIGEgdGFibGUKCiMgTG9hZCBwYWNrYWdlcyAtLS0tLS0KbGlicmFyeShiaW9tYVJ0KSAjIGFubm90YXRlIGdlbmVzIHVzaW5nIGJpb01hcnQKI2xpYnJhcnkoYmlvbWFydHIpICMgZXh0ZW5kaW5nIGJpb21hcnQgYW5ub3RhdGlvbiBsYW5ndWFnZQoKCiMgR2V0IEMuIGVsZWdhbnMgaG9tb2xvZ3MgZm9yIFMuIHN0ZXJjb3JhbGlzIGdlbmVzIGZyb20gQmlvTWFydCBhbmQgZmlsdGVyIC0tLS0tCkFubnQuaW1wb3J0IDwtIGdldEJNKGF0dHJpYnV0ZXM9Yygnd2Jwc19nZW5lX2lkJywgJ2NhZWxlZ3Byam5hMTM3NThfZ2VuZV9uYW1lJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdjYWVsZWdwcmpuYTEzNzU4X2hvbW9sb2dfcGVyY19pZF9yMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnZGVzY3JpcHRpb24nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2ludGVycHJvX3Nob3J0X2Rlc2NyaXB0aW9uJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdnb19uYW1lXzEwMDYnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3VuaXByb3Rfc3B0cmVtYmwnKSwKICAgICAgICAgICAgICAgICAgICAgIyBncmFiIHRoZSBlbnNlbWJsIGFubm90YXRpb25zIGZvciBXb3JtYmFzZSBQYXJhc2l0ZSBnZW5lcwogICAgICAgICAgICAgICAgICAgICBtYXJ0ID0gdXNlTWFydChiaW9tYXJ0PSJwYXJhc2l0ZV9tYXJ0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFzZXQgPSAid2Jwc19nZW5lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvc3Q9Imh0dHBzOi8vcGFyYXNpdGUud29ybWJhc2Uub3JnIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcnQgPSA0NDMpLAogICAgICAgICAgICAgICAgICAgICBmaWx0ZXJzID0gYygnc3BlY2llc19pZF8xMDEwJyksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gbGlzdCgnc3RzdGVycHJqZWI1MjgnKSkgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgI3dlIG5lZWQgdG8gcmVuYW1lIHRoZSB0d28gY29sdW1ucyB3ZSBqdXN0IHJldHJlaXZlZCBmcm9tIGJpb21hcnQKICBkcGx5cjo6cmVuYW1lKGdlbmVJRCA9IHdicHNfZ2VuZV9pZCwKICAgICAgICAgICAgICAgIENlX2dlbmVJRCA9IGNhZWxlZ3Byam5hMTM3NThfZ2VuZV9uYW1lLAogICAgICAgICAgICAgICAgRGVzY3JpcHRpb24gPSBkZXNjcmlwdGlvbiwKICAgICAgICAgICAgICAgIHBlcmNlbnRfaG9tb2xvZ3kgPSBjYWVsZWdwcmpuYTEzNzU4X2hvbW9sb2dfcGVyY19pZF9yMSwKICAgICAgICAgICAgICAgIEdPX3Rlcm0gPSBnb19uYW1lXzEwMDYsCiAgICAgICAgICAgICAgICBVbmlQcm90S0IgPSB1bmlwcm90X3NwdHJlbWJsCiAgKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZ2VuZUlEKQoKIyBSZXBsYWNlIGVtcHR5IHN0cmluZyB2YWx1ZXMgKG1vc3RseSBpbiBDZV9nZW5lSUQgY29sdW1uKSB3aXRoIE5BcwpBbm50LmltcG9ydFtBbm50LmltcG9ydCA9PSAiIl08LU5BCgojIFJlbW92ZSBhbnkgZHVwbGljYXRpb25zIGluIHRoZSBwb3NzaWJsZSBDLiBlbGVnYW5zIGdlbmUgaG9tb2xvZyBtYXRjaGVzLiBTZWxlY3QgYmFzZWQgb24gaGlnaGVzdCAlIGhvbW9sb2d5LgpBbm50LmltcG9ydCRwZXJjZW50X2hvbW9sb2d5W2lzLm5hKEFubnQuaW1wb3J0JHBlcmNlbnRfaG9tb2xvZ3kpXSA8LSAxMDAwICNHaXZlIGZha2UgdmFsdWUgaGVyZSB0byBtYWtlIHN1cmUgZ2VuZXMgd2l0aG91dCBhIENlIGhvbW9sb2cgYXJlbid0IGZpbHRlcmVkIG91dAoKQW5udC5sb2dzIDwtQW5udC5pbXBvcnQgJT4lCiAgZHBseXI6OnNlbGVjdCghYyhpbnRlcnByb19zaG9ydF9kZXNjcmlwdGlvbjpHT190ZXJtKSklPiUKICBncm91cF9ieShnZW5lSUQpICU+JQogIHNsaWNlX21heChuID0gMSwgb3JkZXJfYnkgPSBwZXJjZW50X2hvbW9sb2d5LCB3aXRoX3RpZXMgPSBGQUxTRSkgJT4lCiAgZ3JvdXBfYnkoZ2VuZUlELCBDZV9nZW5lSUQpIAoKIyBSZW1vdmUgc291cmNlIGNvZGUgdG8gc2hvcnRlbiB0aGUgZGVzY3JpcHRpb24KQW5udC5sb2dzJERlc2NyaXB0aW9uPC0gQW5udC5sb2dzJERlc2NyaXB0aW9uICU+JQogIHN0cl9yZXBsYWNlX2FsbChzdHJpbmcgPSAuLCBwYXR0ZXJuID0gIiAgXFxbU291cmNlOi4qXFxdIiwgcmVwbGFjZW1lbnQgPSAiIikgJT4lCiAgY2JpbmQoKQoKQW5udC5sb2dzJHBlcmNlbnRfaG9tb2xvZ3lbQW5udC5sb2dzJHBlcmNlbnRfaG9tb2xvZ3kgPT0gMTAwMF0gPC0gTkEKCiMgQ2xlYW4gdXAgaW50ZXJwcm90S0IgdGVybXMsIHJlbW92aW5nIGR1cGxpY2F0aW9ucyBhbmQgY29sbGFwc2luZyB0byBvbmUgbGluZQpBbm50LmludGVycHJvPC1Bbm50LmltcG9ydCAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbmVJRCwgQ2VfZ2VuZUlELCBpbnRlcnByb19zaG9ydF9kZXNjcmlwdGlvbikgJT4lCiAgZ3JvdXBfYnkoZ2VuZUlELCBDZV9nZW5lSUQpICU+JQogIGRwbHlyOjpkaXN0aW5jdChpbnRlcnByb19zaG9ydF9kZXNjcmlwdGlvbiwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShJbnRlclBybyA9IHBhc3RlKGludGVycHJvX3Nob3J0X2Rlc2NyaXB0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAnLCAnKSkgCiMgQ2xlYW4gdXAgR08gdGVybXMsIHJlbW92aW5nIGR1cGxpY2F0aW9ucyBhbmQgY29sbGFwc2luZyB0byBvbmUgbGluZQpBbm50LmdvdGVybXM8LUFubnQuaW1wb3J0ICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZUlELCBDZV9nZW5lSUQsIEdPX3Rlcm0pICU+JQogIGdyb3VwX2J5KGdlbmVJRCwgQ2VfZ2VuZUlEKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoR09fdGVybSwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShHT190ZXJtID0gcGFzdGUoR09fdGVybSwgY29sbGFwc2UgPSAnLCAnKSkKCmFubm90YXRpb25zPC1kcGx5cjo6bGVmdF9qb2luKEFubnQubG9ncywgQW5udC5pbnRlcnBybykgJT4lCiAgZHBseXI6OmxlZnRfam9pbiguLEFubnQuZ290ZXJtcykgJT4lCiAgdW5ncm91cCgpICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAiZ2VuZUlEIikKCmBgYAoKIyBEYXRhIEZpbHRlcmluZyBhbmQgTm9ybWFsaXphdGlvbiAtLS0KYGBge3IgZGF0YVdyYW5nbGluZywgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFfQojIEdvYWxzIG9mIHRoaXMgY2h1bms6CiMgMSAtIEZpbHRlciBhbmQgbm9ybWFsaXplIGRhdGEKIyAyIC0gdXNlIGdncGxvdDIgdG8gdmlzdWFsaXplIHRoZSBpbXBhY3Qgb2YgZmlsdGVyaW5nIGFuZCBub3JtYWxpemF0aW9uIG9uIHRoZSBkYXRhLgoKIyBOb3RlczoKIyByZWNhbGwgdGhhdCBhYnVuZGFuY2UgZGF0YSBhcmUgVFBNLCB3aGlsZSB0aGUgY291bnRzIGFyZSByZWFkIGNvdW50cyBtYXBwaW5nIHRvIGVhY2ggZ2VuZSBvciB0cmFuc2NyaXB0CgojIExvYWQgcGFja2FnZXMgLS0tLS0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICBsaWJyYXJ5KGVkZ2VSKSAKICBsaWJyYXJ5KG1hdHJpeFN0YXRzKQogIGxpYnJhcnkoY293cGxvdCkKICBsaWJyYXJ5KGdndGhlbWVzKQogIGxpYnJhcnkoUkNvbG9yQnJld2VyKQp9KQoKIyBHZW5lcmF0ZSBhbmQgcGxvdCBzdW1tYXJ5IHN0YXRzIGZvciB0aGUgZGF0YSAtLS0tCm15VFBNLnN0YXRzIDwtIHRyYW5zZm9ybShUeGlfZ2VuZSRhYnVuZGFuY2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgU0Q9cm93U2RzKFR4aV9nZW5lJGFidW5kYW5jZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgQVZHPXJvd01lYW5zKFR4aV9nZW5lJGFidW5kYW5jZSksCiAgICAgICAgICAgICAgICAgICAgICAgICBNRUQ9cm93TWVkaWFucyhUeGlfZ2VuZSRhYnVuZGFuY2UpKQoKIyBwcm9kdWNlIGEgc2NhdHRlciBwbG90IG9mIHRoZSB0cmFuc2Zvcm1lZCBkYXRhCnAxPC1nZ3Bsb3QobXlUUE0uc3RhdHMpICsgCiAgYWVzKHggPSBTRCwgeSA9IE1FRCkgKwogIGdlb21fcG9pbnQoc2hhcGU9MTYsIHNpemU9MiwgYWxwaGEgPSAwLjIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0pICsKICAjZ2VvbV9oZXgoc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGxhYnMoeT0iTWVkaWFuIiwgeCA9ICJTdGFuZGFyZCBkZXZpYXRpb24iLAogICAgICAgdGl0bGU9IlRyYW5zY3JpcHRzIHBlciBtaWxsaW9uIChUUE0pIiwKICAgICAgIHN1YnRpdGxlPSJ1bmZpbHRlcmVkLCBub24tbm9ybWFsaXplZCBkYXRhIiwKICAgICAgIGNhcHRpb249IlMuIHN0ZXJjb3JhbGlzIFN0b2x0emZ1cyBSTkFzZXEgRGF0YXNldCIpICsKICB0aGVtZV9idygpCnAxCgojIG1ha2UgYSBEaWdpdGFsIEdlbmUgRXhwcmVzc2lvbiBsaXN0IHVzaW5nIHRoZSByYXcgY291bnRzIGFuZCBwbG90IC0tLS0KbXlER0VMaXN0IDwtIERHRUxpc3QoVHhpX2dlbmUkY291bnRzLCAKICAgICAgICAgICAgICAgICAgICAgc2FtcGxlcyA9IHRhcmdldHMkc2FtcGxlLCAKICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSB0YXJnZXRzJGdyb3VwLAogICAgICAgICAgICAgICAgICAgICBnZW5lcyA9IGFubm90YXRpb25zKQoKIyBjYWxjdWxhdGUgYW5kIHBsb3QgbG9nMiBjb3VudHMgcGVyIG1pbGxpb24gLS0tLQoKIyBjYXB0dXJlIHNhbXBsZSBsYWJlbHMgZnJvbSB0aGUgc3R1ZHkgZGVzaWduIGZpbGUKc2FtcGxlTGFiZWxzIDwtIHRhcmdldHMkc2FtcGxlCgojIGNhcHR1cmUgZ3JvdXAgbGFiZWxzIGZyb20gdGhlIHN0dWR5IGRlc2lnbiBmaWxlCmdyb3VwTGFiZWxzIDwtIHRhcmdldHMkZ3JvdXAKCiMgR2VuZXJhdGUgbGlmZSBzdGFnZSBJRHMKaWRzIDwtIHJlcChjYmluZCh0YXJnZXRzJGdyb3VwKSwgCiAgICAgICAgICAgdGltZXMgPSBucm93KFR4LlNzKSkgJT4lCiAgYXNfZmFjdG9yKCkKCiMgdXNlIHRoZSAnY3BtJyBmdW5jdGlvbiBmcm9tIEVkZ2VSIHRvIGdldCBsb2cyIGNvdW50cyBwZXIgbWlsbGlvbgojIHRoZW4gY29lcmNlIGludG8gYSB0aWJibGUKIyBhZGQgc2FtcGxlIG5hbWVzIHRvIHRoZSBkYXRhZnJhbWUKIyB0aWR5IHVwIHRoZSBkYXRhZnJhbWUgaW50byBhIHRpYmJsZQpsb2cyLmNwbS5kZi5waXZvdCA8LWNwbShteURHRUxpc3QsIGxvZz1UUlVFKSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIikgJT4lCiAgc2V0TmFtZXMobm0gPSBjKCJnZW5lSUQiLCBzYW1wbGVMYWJlbHMpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1nZW5lSUQsIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJzYW1wbGVzIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJleHByZXNzaW9uIikgJT4lIAogIGFkZF9jb2x1bW4obGlmZV9zdGFnZSA9IGlkcykKCgojIHBsb3QgdGhlIHBpdm90ZWQgZGF0YQpwMiA8LSBnZ3Bsb3QobG9nMi5jcG0uZGYucGl2b3QpICsKICBhZXMoeD1zYW1wbGVzLCB5PWV4cHJlc3Npb24sIGZpbGw9bGlmZV9zdGFnZSkgKwogIGdlb21fdmlvbGluKHRyaW0gPSBGQUxTRSwgc2hvdy5sZWdlbmQgPSBULCBhbHBoYT0gMC43KSArCiAgc3RhdF9zdW1tYXJ5KGZ1biA9ICJtZWRpYW4iLCAKICAgICAgICAgICAgICAgZ2VvbSA9ICJwb2ludCIsIAogICAgICAgICAgICAgICBzaGFwZSA9IDIwLCAKICAgICAgICAgICAgICAgc2l6ZSA9IDIsIAogICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyh5PSJsb2cyIGV4cHJlc3Npb24iLCB4ID0gInNhbXBsZSIsCiAgICAgICB0aXRsZT0iTG9nMiBDb3VudHMgcGVyIE1pbGxpb24gKENQTSkiLAogICAgICAgc3VidGl0bGU9InVuZmlsdGVyZWQsIG5vbi1ub3JtYWxpemVkIiwKICAgICAgIGNhcHRpb249cGFzdGUwKCJwcm9kdWNlZCBvbiAiLCBTeXMudGltZSgpKSkgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgY29vcmRfZmxpcCgpCnAyCgojIEZpbHRlciB0aGUgZGF0YSAtLS0tCiMgVGFrZSBhIGxvb2sgYXQgaG93IG1hbnkgZ2VuZXMgb3IgdHJhbnNjcmlwdHMgaGF2ZSBubyByZWFkIGNvdW50cyBpbiBhbGwgb2YgdGhlIHNhbXBsZXMKI3RhYmxlKHJvd1N1bXMobXlER0VMaXN0JGNvdW50cz09MCk9PW5yb3codGFyZ2V0cykpCgojIG5vdyBzZXQgc29tZSBjdXQtb2ZmIHRvIGdldCByaWQgb2YgZ2VuZXMvdHJhbnNjcmlwdHMgd2l0aCBsb3cgY291bnRzCiMgYWdhaW4gdXNpbmcgcm93U3VtcyB0byB0YWxseSB1cCB0aGUgJ1RSVUUnIHJlc3VsdHMgb2YgYSBzaW1wbGUgZXZhbHVhdGlvbgojIGhvdyBtYW55IGdlbmVzIGhhZCBtb3JlIHRoYW4gMSBDUE0gKFRSVUUpIGluIGF0IGxlYXN0IG4gc2FtcGxlcwojIFRoZSBjdXRvZmYgIm4iIHNob3VsZCBiZSBhZGp1c3RlZCBmb3IgdGhlIG51bWJlciBvZiBzYW1wbGVzIGluIHRoZSBzbWFsbGVzdCBncm91cCBvZiBjb21wYXJpc29uLgprZWVwZXJzIDwtIGNwbShteURHRUxpc3QpICU+JQogIHJvd1N1bXMoLj4xKT49MwoKIyBub3cgdXNlIGJhc2UgUidzIHNpbXBsZSBzdWJzZXR0aW5nIG1ldGhvZCB0byBmaWx0ZXIgdGhlIERHRUxpc3QgYmFzZWQgb24gdGhlIGxvZ2ljYWwgcHJvZHVjZWQgYWJvdmUKbXlER0VMaXN0LmZpbHRlcmVkIDwtIG15REdFTGlzdFtrZWVwZXJzLF0KI2RpbShteURHRUxpc3QuZmlsdGVyZWQpCgppZHMuZmlsdGVyZWQgPC0gcmVwKGNiaW5kKHRhcmdldHMkZ3JvdXApLCAKICAgICAgICAgICAgICAgICAgICB0aW1lcyA9IG5yb3cobXlER0VMaXN0LmZpbHRlcmVkKSkgJT4lCiAgYXNfZmFjdG9yKCkKCmxvZzIuY3BtLmZpbHRlcmVkLmRmLnBpdm90IDwtIGNwbShteURHRUxpc3QuZmlsdGVyZWQsIGxvZz1UUlVFKSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIikgJT4lCiAgc2V0TmFtZXMobm0gPSBjKCJnZW5lSUQiLCBzYW1wbGVMYWJlbHMpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1nZW5lSUQsCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInNhbXBsZXMiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiZXhwcmVzc2lvbiIpICU+JQogIGFkZF9jb2x1bW4obGlmZV9zdGFnZSA9IGlkcy5maWx0ZXJlZCkKCnAzIDwtIGdncGxvdChsb2cyLmNwbS5maWx0ZXJlZC5kZi5waXZvdCkgKwogIGFlcyh4PXNhbXBsZXMsIHk9ZXhwcmVzc2lvbiwgZmlsbD1saWZlX3N0YWdlKSArCiAgZ2VvbV92aW9saW4odHJpbSA9IEZBTFNFLCBzaG93LmxlZ2VuZCA9IFQsIGFscGhhPSAwLjcpICsKICBzdGF0X3N1bW1hcnkoZnVuID0gIm1lZGlhbiIsIAogICAgICAgICAgICAgICBnZW9tID0gInBvaW50IiwgCiAgICAgICAgICAgICAgIHNoYXBlID0gMjAsIAogICAgICAgICAgICAgICBzaXplID0gMiwgCiAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBsYWJzKHk9ImxvZzIgZXhwcmVzc2lvbiIsIHggPSAic2FtcGxlIiwKICAgICAgIHRpdGxlPSJMb2cyIENvdW50cyBwZXIgTWlsbGlvbiAoQ1BNKSIsCiAgICAgICBzdWJ0aXRsZT0iZmlsdGVyZWQsIG5vbi1ub3JtYWxpemVkIiwKICAgICAgIGNhcHRpb249cGFzdGUwKCJwcm9kdWNlZCBvbiAiLCBTeXMudGltZSgpKSkgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgY29vcmRfZmxpcCgpCnAzCgojIE5vcm1hbGl6ZSB0aGUgZGF0YSB1c2luZyBhIGJldHdlZW4gc2FtcGxlcyBub3JtYWxpemF0aW9uIC0tLS0KIyBTb3VyY2UgZm9yIFRNTSBzYW1wbGUgbm9ybWFsaXphdGlvbiBoZXJlOiBodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9nYi0yMDEwLTExLTMtcjI1Cm15REdFTGlzdC5maWx0ZXJlZC5ub3JtIDwtIGNhbGNOb3JtRmFjdG9ycyhteURHRUxpc3QuZmlsdGVyZWQsIG1ldGhvZCA9ICJUTU0iKQoKbG9nMi5jcG0uZmlsdGVyZWQubm9ybS5kZjwtIGNwbShteURHRUxpc3QuZmlsdGVyZWQubm9ybSwgbG9nPVRSVUUpICU+JQogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lSUQiKSAlPiUKICBzZXROYW1lcyhubSA9IGMoImdlbmVJRCIsIHNhbXBsZUxhYmVscykpCgpsb2cyLmNwbS5maWx0ZXJlZC5ub3JtLmRmLnBpdm90PC1sb2cyLmNwbS5maWx0ZXJlZC5ub3JtLmRmICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLWdlbmVJRCwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAic2FtcGxlcyIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJleHByZXNzaW9uIikgJT4lCiAgYWRkX2NvbHVtbihsaWZlX3N0YWdlID0gaWRzLmZpbHRlcmVkKQoKcDQgPC0gZ2dwbG90KGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0uZGYucGl2b3QpICsKICBhZXMoeD1zYW1wbGVzLCB5PWV4cHJlc3Npb24sIGZpbGw9bGlmZV9zdGFnZSkgKwogIGdlb21fdmlvbGluKHRyaW0gPSBGQUxTRSwgc2hvdy5sZWdlbmQgPSBULCBhbHBoYSA9IDAuNykgKwogIHN0YXRfc3VtbWFyeShmdW4gPSAibWVkaWFuIiwgCiAgICAgICAgICAgICAgIGdlb20gPSAicG9pbnQiLCAKICAgICAgICAgICAgICAgc2hhcGUgPSAyMCwgCiAgICAgICAgICAgICAgIHNpemUgPSAyLCAKICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGxhYnMoeT0ibG9nMiBleHByZXNzaW9uIiwgeCA9ICJzYW1wbGUiLAogICAgICAgdGl0bGU9IkxvZzIgQ291bnRzIHBlciBNaWxsaW9uIChDUE0pIiwKICAgICAgIHN1YnRpdGxlPSJmaWx0ZXJlZCwgVE1NIG5vcm1hbGl6ZWQiLAogICAgICAgY2FwdGlvbj1wYXN0ZTAoInByb2R1Y2VkIG9uICIsIFN5cy50aW1lKCkpKSArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsKICBjb29yZF9mbGlwKCkKcDQKCiMgUGxvdCBhbGwgdGhlIHBsb3RzIG9uIGEgZ3JpZApwNSA8LSBwbG90X2dyaWQocDEsIHAyLCBwMywgcDQsIGxhYmVscyA9IGMoJ0EnLCAnQicsICdDJywgJ0QnKSwgbGFiZWxfc2l6ZSA9IDEyKQojIGdnc2F2ZSgiU3Ryb25neWxvaWRlcyBzdGVyY29yYWxpcyBSTkFzZXEgQ291bnRzLnBkZiIsIAojICAgICAgICBwbG90ID0gcDUsIAojICAgICAgICBkZXZpY2UgPSAicGRmIiwKIyAgICAgICAgaGVpZ2h0ID0gOC41LAojICAgICAgICB3aWR0aCA9IDExLAojICAgICAgICBwYXRoID0gJy4vT3V0cHV0cy8nKQojIAoKCmBgYAoKIyBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyBhbmQgUHJpbmNpcGxlIENvbXBvbmVudHMgQW5hbHlzaXMgLS0tCmBgYHtyIG11bHRpdmFyaWF0ZX0KIyBJbnRyb2R1Y3Rpb24gdG8gdGhpcyBjaHVuayAtLS0tLS0tLS0tLQojIFRoaXMgY29kZSBjaHVuayBzdGFydHMgd2l0aCBmaWx0ZXJlZCBhbmQgbm9ybWFsaXplZCBhYnVuZGFuY2UgZGF0YSBpbiBhIGRhdGEgZnJhbWUgKG5vdCB0aWR5KS4KIyBJdCB3aWxsIGltcGxlbWVudCBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBhbmQgUENBIGFuYWx5c2VzIG9uIHRoZSBkYXRhLgojIEl0IHdpbGwgcGxvdCB2YXJpb3VzIGdyYXBocyBhbmQgY2FuIHNhdmUgdGhlbSBpbiBQREYgZmlsZXMuCiMgTG9hZCBwYWNrYWdlcyAtLS0tLS0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KHRpZHl2ZXJzZSkgIyB5b3UncmUgZmFtaWxpYXIgd2l0aCB0aGlzIGZyb210IHRoZSBwYXN0IHR3byBsZWN0dXJlcwogIGxpYnJhcnkoZ2dwbG90MikKICBsaWJyYXJ5KFJDb2xvckJyZXdlcikKICBsaWJyYXJ5KGdnZGVuZHJvKQogIGxpYnJhcnkobWFncml0dHIpCiAgbGlicmFyeShmYWN0b2V4dHJhKQogIGxpYnJhcnkoZ3JpZEV4dHJhKQogIGxpYnJhcnkoY293cGxvdCkKICBsaWJyYXJ5KGRlbmRleHRlbmQpCn0pCgojIElkZW50aWZ5IHZhcmlhYmxlcyBvZiBpbnRlcmVzdCBpbiBzdHVkeSBkZXNpZ24gZmlsZSAtLS0tCmdyb3VwIDwtIGZhY3Rvcih0YXJnZXRzJGdyb3VwKQoKIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZyAtLS0tLS0tLS0tLS0tLS0KIyBSZW1lbWJlcjogaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgY2FuIG9ubHkgd29yayBvbiBhIGRhdGEgbWF0cml4LCBub3QgYSBkYXRhIGZyYW1lCgojIENvbnZlcnQgbG9nMiBjcG0gZmlsdGVyZWQsIG5vcm1hbGl6ZWQgZGF0YSBmcmFtZSBpbnRvIGEgbWF0cml4CmxvZzIuY3BtLmZpbHRlcmVkLm5vcm0gPC0gbG9nMi5jcG0uZmlsdGVyZWQubm9ybS5kZiAlPiUKICBkcGx5cjo6c2VsZWN0KCFnZW5lSUQpICU+JQogIGRhdGEubWF0cml4KCkKcm93bmFtZXMobG9nMi5jcG0uZmlsdGVyZWQubm9ybSkgPC0gbG9nMi5jcG0uZmlsdGVyZWQubm9ybS5kZiRnZW5lSUQKCgojIENhbGN1bGF0ZSBkaXN0YW5jZSBtYXRyaXgKIyBkaXN0IGNhbGN1bGF0ZXMgZGlzdGFuY2UgYmV0d2VlbiByb3dzLCBzbyB0cmFuc3Bvc2UgZGF0YSBzbyB0aGF0IHdlIGdldCBkaXN0YW5jZSBiZXR3ZWVuIHNhbXBsZXMuCiMgaG93IHNpbWlsYXIgYXJlIHNhbXBsZXMgZnJvbSBlYWNoIG90aGVyCmNvbG5hbWVzKGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0pPC10YXJnZXRzJGdyb3VwCmRpc3RhbmNlIDwtIGRpc3QodChsb2cyLmNwbS5maWx0ZXJlZC5ub3JtKSwgbWV0aG9kID0gIm1heGltdW0iKSAjb3RoZXIgZGlzdGFuY2UgbWV0aG9kcyBhcmUgImV1Y2xpZGVhbiIsIG1heGltdW0iLCAibWFuaGF0dGFuIiwgImNhbmJlcnJhIiwgImJpbmFyeSIgb3IgIm1pbmtvd3NraSIKCiMgQ2FsY3VsYXRlIGNsdXN0ZXJzIHRvIHZpc3VhbGl6ZSBkaWZmZXJlbmNlcy4gVGhpcyBpcyB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcuCiMgVGhlIG1ldGhvZHMgaGVyZSBpbmNsdWRlOiBzaW5nbGUgKGkuZS4gImZyaWVuZHMtb2YtZnJpZW5kcyIpLCBjb21wbGV0ZSAoaS5lLiBjb21wbGV0ZSBsaW5rYWdlKSwgYW5kIGF2ZXJhZ2UgKGkuZS4gVVBHTUEpLiBIZXJlJ3MgYSBjb21wYXJpc29uIG9mIGRpZmZlcmVudCB0eXBlczogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVVBHTUEjQ29tcGFyaXNvbl93aXRoX290aGVyX2xpbmthZ2VzCmNsdXN0ZXJzIDwtIGhjbHVzdChkaXN0YW5jZSwgbWV0aG9kID0gImNvbXBsZXRlIikgI290aGVyIGFnZ2xvbWVyYXRpb24gbWV0aG9kcyBhcmUgIndhcmQuRCIsICJ3YXJkLkQyIiwgInNpbmdsZSIsICJjb21wbGV0ZSIsICJhdmVyYWdlIiwgIm1jcXVpdHR5IiwgIm1lZGlhbiIsIG9yICJjZW50cm9pZCIKZGVuZCA8LSBhcy5kZW5kcm9ncmFtKGNsdXN0ZXJzKSAKCnAxPC1kZW5kICU+JSAKICBkZW5kZXh0ZW5kOjpzZXQoImJyYW5jaGVzX2tfY29sb3IiLCBrID0gNSkgJT4lIAogIGRlbmRleHRlbmQ6OnNldCgiaGFuZ19sZWF2ZXMiLCBjKDAuMSkpICU+JSAKICBkZW5kZXh0ZW5kOjpzZXQoImxhYmVsc19jZXgiLCBjKDAuNSkpICU+JQogIGRlbmRleHRlbmQ6OnNldCgibGFiZWxzX2NvbG9ycyIsIGsgPSA1KSAlPiUgCiAgZGVuZGV4dGVuZDo6c2V0KCJicmFuY2hlc19sd2QiLCBjKDAuNykpICU+JSAKICAKICBhcy5nZ2RlbmQgJT4lCiAgZ2dwbG90IChvZmZzZXRfbGFiZWxzID0gLTAuNSkgKwogIHRoZW1lX2RlbmRybygpICsKICB5bGltKDAsIG1heChnZXRfYnJhbmNoZXNfaGVpZ2h0cyhkZW5kKSkpICsKICBsYWJzKHRpdGxlID0gIkhpZXJhcmNoaWNhbCBDbHVzdGVyIERlbmRyb2dyYW0gIiwKICAgICAgIHN1YnRpdGxlID0gImZpbHRlcmVkLCBUTU0gbm9ybWFsaXplZCIsCiAgICAgICB5ID0gIkRpc3RhbmNlIiwKICAgICAgIHggPSAiTGlmZSBzdGFnZSIpICsKICBjb29yZF9maXhlZCgxLzIpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSwKICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGlja3MubGVuZ3RoLnkgPSB1bml0KDIsICJtbSIpKQpwMQoKIyBQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIC0tLS0tLS0tLS0tLS0KIyB0aGlzIGFsc28gd29ya3Mgb24gYSBkYXRhIG1hdHJpeCwgbm90IGEgZGF0YSBmcmFtZQpwY2EucmVzIDwtIHByY29tcCh0KGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0pLCBzY2FsZS49RiwgcmV0eD1UKQpzdW1tYXJ5KHBjYS5yZXMpICMgUHJpbnRzIHZhcmlhbmNlIHN1bW1hcnkgZm9yIGFsbCBwcmluY2lwYWwgY29tcG9uZW50cy4KCiNwY2EucmVzJHJvdGF0aW9uICMkcm90YXRpb24gc2hvd3MgeW91IGhvdyBtdWNoIGVhY2ggZ2VuZSBpbmZsdWVuY2VkIGVhY2ggUEMgKGNhbGxlZCAnc2NvcmVzJykKI3BjYS5yZXMkeCAjICd4JyBzaG93cyB5b3UgaG93IG11Y2ggZWFjaCBzYW1wbGUgaW5mbHVlbmNlZCBlYWNoIFBDIChjYWxsZWQgJ2xvYWRpbmdzJykKI25vdGUgdGhhdCB0aGVzZSBoYXZlIGEgbWFnbml0dWRlIGFuZCBhIGRpcmVjdGlvbiAodGhpcyBpcyB0aGUgYmFzaXMgZm9yIG1ha2luZyBhIFBDQSBwbG90KQojIyBUaGlzIGdlbmVyYXRlcyBhIHNjcmVlcGxvdDogYSBzdGFuZGFyZCB3YXkgdG8gdmlldyBlaWdlbnZhbHVlcyBmb3IgZWFjaCBQQ0EuIFNob3dzIHRoZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGFjY291bnRlZCBmb3IgYnkgZWFjaCBQQy4gUGxvdHRpbmcgb25seSB0aGUgZmlyc3QgMTAgZGltZW5zaW9ucy4KcDI8LWZ2aXpfZWlnKHBjYS5yZXMsCiAgICAgICAgICAgICBiYXJjb2xvciA9IGJyZXdlci5wYWwoOCwiUGFzdGVsMiIpWzhdLAogICAgICAgICAgICAgYmFyZmlsbCA9IGJyZXdlci5wYWwoOCwiUGFzdGVsMiIpWzhdLAogICAgICAgICAgICAgbGluZWNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgIG1haW4gPSAiU2NyZWUgcGxvdDogcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBhY2NvdW50ZWQgZm9yIGJ5IGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudCIsCiAgICAgICAgICAgICBnZ3RoZW1lID0gdGhlbWVfYncoKSkgCnAyCgpwYy52YXI8LXBjYS5yZXMkc2Rldl4yICMgc2Rldl4yIGNhcHR1cmVzIHRoZXNlIGVpZ2VudmFsdWVzIGZyb20gdGhlIFBDQSByZXN1bHQKcGMucGVyPC1yb3VuZChwYy52YXIvc3VtKHBjLnZhcikqMTAwLCAxKSAjIHdlIGNhbiB0aGVuIHVzZSB0aGVzZSBlaWdlbnZhbHVlcyB0byBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2UgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggUEMKCiMgVmlzdWFsaXplIHRoZSBQQ0EgcmVzdWx0IC0tLS0tLS0tLS0tLS0tLS0tLQojbGV0cyBmaXJzdCBwbG90IGFueSB0d28gUENzIGFnYWluc3QgZWFjaCBvdGhlcgojV2Uga25vdyBob3cgbXVjaCBlYWNoIHNhbXBsZSBjb250cmlidXRlcyB0byBlYWNoIFBDIChsb2FkaW5ncyksIHNvIGxldCdzIHBsb3QKcGNhLnJlcy5kZiA8LSBhc190aWJibGUocGNhLnJlcyR4KQoKIyBQbG90dGluZyBQQzEgYW5kIFBDMgpwMzwtZ2dwbG90KHBjYS5yZXMuZGYpICsKICBhZXMoeD1QQzEsIHk9UEMyLCBsYWJlbD1ncm91cExhYmVscywgCiAgICAgIGZpbGwgPSBncm91cExhYmVscywKICAgICAgY29sb3IgPSBncm91cExhYmVscwogICkgKwogIGdlb21fcG9pbnQoc2l6ZT00LCBzaGFwZT0gMjEsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsKICAjZ2VvbV9sYWJlbChjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiLCBndWlkZSA9IEZBTFNFKSArCiAgI3N0YXRfZWxsaXBzZSgpICsKICB4bGFiKHBhc3RlMCgiUEMxICgiLHBjLnBlclsxXSwiJSIsIikiKSkgKyAKICB5bGFiKHBhc3RlMCgiUEMyICgiLHBjLnBlclsyXSwiJSIsIikiKSkgKwogIGxhYnModGl0bGU9IlByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzIG9mIFMuIHN0ZXJjb3JhbGlzIFJOQXNlcSBTYW1wbGVzIiwKICAgICAgIHN1YiA9ICJOb3RlOiBhbmFseXNpcyBpcyBibGluZCB0byBsaWZlIHN0YWdlIGlkZW50aXR5LiIsCiAgICAgICBmaWxsID0gIkxpZmUgU3RhZ2UiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoLjMsIC4zKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKC4zLCAuMykpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9idygpCnAzCgojIEEgZ3Vlc3MgYXQgdGhlIGlkZW50aXR5IG9mIFBDMQpwNDwtZ2dwbG90KHBjYS5yZXMuZGYpICsKICBhZXMoeD1QQzEsIHk9UEMyLCBsYWJlbD1ncm91cExhYmVscywKICAgICAgY29sb3IgPSBmYWN0b3IodGFyZ2V0cyRNYXR1cml0eSksCiAgICAgIGZpbGwgPSBmYWN0b3IodGFyZ2V0cyRNYXR1cml0eSkKICApICsKICAjZ2VvbV9wb2ludChzaXplPTQpICsKICAjc3RhdF9lbGxpcHNlKCkgKwogIGdlb21fbGFiZWwoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMiwgYWxwaGEgPSAwLjcpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBicmV3ZXIucGFsKDgsIlNldDIiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBicmV3ZXIucGFsKDgsIlNldDIiKSwgZ3VpZGUgPSBGQUxTRSkgKwogIHhsYWIocGFzdGUwKCJQQzEgKCIscGMucGVyWzFdLCIlIiwiKSIpKSArIAogIHlsYWIocGFzdGUwKCJQQzIgKCIscGMucGVyWzJdLCIlIiwiKSIpKSArCiAgbGFicyhzdWJ0aXRsZT0iUG90ZW50aWFsIFBDMSBpZGVudGl0eTogQWR1bHRzIHZzIExhcnZhZSIsCiAgICAgICBmaWxsID0gIk1hdHVyaXR5IikgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKC4zLCAuMykpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYyguMywgLjMpKSArCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfYncoKQpwNAoKIyBBIGd1ZXNzIGF0IHRoZSBpZGVudGl0eSBvZiBQQzIKcDU8LWdncGxvdChwY2EucmVzLmRmKSArCiAgYWVzKHg9UEMxLCB5PVBDMiwgbGFiZWw9Z3JvdXBMYWJlbHMsIAogICAgICBjb2xvciA9IGZhY3Rvcih0YXJnZXRzJEluZmVjdGlvdXMpLAogICAgICBmaWxsID0gZmFjdG9yKHRhcmdldHMkSW5mZWN0aW91cykKICApICsKICAjZ2VvbV9wb2ludChzaXplPTQpICsKICAjc3RhdF9lbGxpcHNlKCkgKwogIGdlb21fbGFiZWwoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMiwgYWxwaGEgPSAwLjcpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBicmV3ZXIucGFsKDgsIlNldDIiKVszOjRdKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGJyZXdlci5wYWwoOCwiU2V0MiIpWzM6NF0sIGd1aWRlID0gRkFMU0UpICsKICB4bGFiKHBhc3RlMCgiUEMxICgiLHBjLnBlclsxXSwiJSIsIikiKSkgKyAKICB5bGFiKHBhc3RlMCgiUEMyICgiLHBjLnBlclsyXSwiJSIsIikiKSkgKwogIGxhYnMoc3VidGl0bGU9IlBvdGVudGlhbCBQQzIgaWRlbnRpdHk6IEhvc3Qtc2Vla2luZy9kd2VsbGluZyIsCiAgICAgICBmaWxsID0gJ0luZmVjdGl2aXR5IFN0YWdlJykgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKC4zLCAuMykpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYyguMywgLjMpKSArCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfYncoKQpwNQoKIyBDcmVhdGUgYSBQQ0EgJ3NtYWxsIG11bHRpcGxlcycgY2hhcnQgLS0tLQpwY2EucmVzLmRmIDwtIHBjYS5yZXMkeFssMTo2XSAlPiUgCiAgYXNfdGliYmxlKCkgJT4lCiAgYWRkX2NvbHVtbihzYW1wbGUgPSBzYW1wbGVMYWJlbHMsCiAgICAgICAgICAgICBncm91cCA9IGdyb3VwLAogICAgICAgICAgICAgbWF0dXJpdHkgPSBmYWN0b3IodGFyZ2V0cyRNYXR1cml0eSksCiAgICAgICAgICAgICBpbmZlY3Rpb3VzID0gZmFjdG9yKHRhcmdldHMkSW5mZWN0aW91cykpCgpwY2EucGl2b3QgPC0gcGl2b3RfbG9uZ2VyKHBjYS5yZXMuZGYsICMgZGF0YWZyYW1lIHRvIGJlIHBpdm90ZWQKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xzID0gUEMxOlBDNiwgIyBjb2x1bW4gbmFtZXMgdG8gYmUgc3RvcmVkIGFzIGEgU0lOR0xFIHZhcmlhYmxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiUEMiLCAjIG5hbWUgb2YgdGhhdCBuZXcgdmFyaWFibGUgKGNvbHVtbikKICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAibG9hZGluZ3MiKSAjIG5hbWUgb2YgbmV3IHZhcmlhYmxlIChjb2x1bW4pIHN0b3JpbmcgYWxsIHRoZSB2YWx1ZXMgKGRhdGEpClBDMTwtc3Vic2V0KHBjYS5waXZvdCwgUEMgPT0gIlBDMSIpClBDMiA8LXN1YnNldChwY2EucGl2b3QsIFBDID09ICJQQzIiKQojUEMzIDwtIHN1YnNldChwY2EucGl2b3QsIFBDID09ICJQQzMiKQojUEM0IDwtIHN1YnNldChwY2EucGl2b3QsIFBDID09ICJQQzQiKQoKcDY8LWdncGxvdChwY2EucGl2b3QpICsKICBhZXMoeD1zYW1wbGUsIHk9bG9hZGluZ3MpICsgIyB5b3UgY291bGQgaXRlcmF0aXZlbHkgJ3BhaW50JyBkaWZmZXJlbnQgY292YXJpYXRlcyBvbnRvIHRoaXMgcGxvdCB1c2luZyB0aGUgJ2ZpbGwnIGFlcwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbCA9IGJyZXdlci5wYWwoOCwiUGFzdGVsMiIpWzhdKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogIGZhY2V0X3dyYXAoflBDKSArCiAgZ2VvbV9iYXIoZGF0YSA9IFBDMSwgc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhmaWxsID0gbWF0dXJpdHkpKSArCiAgZ2VvbV9iYXIoZGF0YSA9IFBDMiwgc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhmaWxsID0gaW5mZWN0aW91cykpICsKICBsYWJzKHRpdGxlPSJQQ0EgJ3NtYWxsIG11bHRpcGxlcycgcGxvdCIsCiAgICAgICBmaWxsID0gIkxpZmUgU3RhZ2UgR3JvdXBzIiwKICAgICAgIGNhcHRpb249cGFzdGUwKCJwcm9kdWNlZCBvbiAiLCBTeXMudGltZSgpKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gdGFyZ2V0cyRzYW1wbGUsIGxhYmVscyA9IHRhcmdldHMkZ3JvdXApICsKICB0aGVtZV9idygpICsKICBjb29yZF9mbGlwKCkKcDYKCiMgR3JhcGggYWxsIHRoZSBwbG90cyBnZW5lcmF0ZWQgaW4gdGhpcyBzY3JpcHQgLS0tLQojIFBsb3QgYWxsIHRoZSBwbG90cyBvbiBhIGdyaWQKIyBwNzwtIGdyaWQuYXJyYW5nZShwMSxwMixwMyxwNCwgcDUscDYsbmNvbCA9IDQsCiMgICAgICAgICAgICAgbGF5b3V0X21hdHJpeCA9IGNiaW5kKGMoMSwxLDEsMiwyLDIpLGMoMywzLDQsNCw1LDUpLGMoNiw2LDYsNiw2LDYpLCBjKDYsNiw2LDYsNiw2KSkpCiMgZ2dzYXZlKCJTdHJvbmd5bG9pZGVzIHN0ZXJjb3JhbGlzIFJOQXNlcSBNdWx0aXZhcmlhdGUgQW5hbHlzaXMucGRmIiwgCiMgICAgICAgIHBsb3QgPSBwNywgCiMgICAgICAgIGRldmljZSA9ICJwZGYiLAojICAgICAgICAjaGVpZ2h0ID0gMTcsCiMgICAgICAgIHdpZHRoID0gMjIsCiMgICAgICAgIHBhdGggPSAnLi9PdXRwdXRzLycKCmBgYAoKIyBHZW5lcyBDb250cmlidXRpbmcgdG8gUEMgSWRlbnRpdHkgLS0tCmBgYHtyIGlkUENnZW5lc30KIyBJbnRyb2R1Y3Rpb24gdG8gdGhpcyBjaHVuayAtLS0tCiMgVGhpcyBjaHVuayByZXR1cm5zIHRvIHRoZSBwcmluY2lwYWwgY29tcG9uZW50cywgaW4gb3JkZXIgdG8gZGV0ZXJtaW4gd2hpY2ggZ2VuZXMgYXJlIGluZmx1ZW5jaW5nIHRoZSBpZGVudGlmaWVkIFBDcy4KIyBJdCBjb21iaW5lcyB0aGUgb3V0cHV0cyBvZiB0aGUgSGllcmFyaWNoYWwgQ2x1c3RlcmluZyBDaHVuaywgd2l0aCB0aGUgdmFyaWFuY2Utc3RhYmlsaXplZCBERUcgdmFsdWVzIHByb2R1Y2VkIGJ5IHZvb20tbGltbWEsIGNhbGN1bGF0ZWQgaW4gdGhlIHBhaXJ3aXNlIGNvbXBhcmlzb25zIGNodW5rLgoKCiMgVXNlIHBjYS5yZXMkcm90YXRpb24gdG8gc2VsZWN0IGdlbmVzIGluZmx1ZW5jaW5nIFBDMS02IC0tLS0KbXlzY29yZXMuZGYgPC0gcGNhLnJlcyRyb3RhdGlvblssMTo2XSAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImdlbmVJRCIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLWdlbmVJRCwgbmFtZXNfdG8gPSAiUEMiLCB2YWx1ZXNfdG8gPSAic2NvcmVzIikgJT4lCiAgZHBseXI6Om11dGF0ZShhYnNfc2NvcmVzID0gYWJzKHNjb3JlcykpICU+JQogIGdyb3VwX2J5KFBDKSAlPiUKICBzbGljZV9tYXgoYWJzX3Njb3JlcywgcHJvcCA9IC4xKSAjIGdldCB0b3AgMTAlIG9mIGdlbmVzIGluIGFsbCBQQ3MKCiMgUHVsbCBvdXQgZ2VuZXMgdGhhdCBhcmUgdGhlIHRvcCAxMCUgb2YgY29udHJpYnV0b3JzIChpbiBhbnkgZGlyZWN0aW9uKSB0byBQQzEgYW5kIFBDMi4gQW5ub3RhdGUuCm15c2NvcmVzLlRvcDEwIDwtIG15c2NvcmVzLmRmICU+JQogIGRwbHlyOjpmaWx0ZXIoUEMgPT0gIlBDMSIgfCBQQyA9PSAiUEMyIikgJT4lCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IGdlbmVJRCwKICAgICAgICAgICAgICBuYW1lc19mcm9tID0gUEMsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBzY29yZXMpICU+JQogIGRwbHlyOjptdXRhdGUoUEMxX2lkID0gY2FzZV93aGVuKFBDMSA+IDAgfiAibGFydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQQzEgPCAwIH4gImFkdWx0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpcy5uYShQQzEpIH4gIi0tIikpICU+JSAgCiAgZHBseXI6Om11dGF0ZShQQzJfaWQgPSBjYXNlX3doZW4oUEMyID4gMCB+ICJwYXJhc2l0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUEMyIDwgMCB+ICJub25fcGFyYXNpdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKFBDMikgfiAiLS0iKSkgJT4lICAKICBkcGx5cjo6bGVmdF9qb2luKC4sKHJvd25hbWVzX3RvX2NvbHVtbihhbm5vdGF0aW9ucywgdmFyID0gImdlbmVJRCIpKSwgYnkgPSAiZ2VuZUlEIikgJT4lCiAgZHBseXI6OnJlbG9jYXRlKFVuaVByb3RLQiwgRGVzY3JpcHRpb24sIEludGVyUHJvLCBHT190ZXJtLCBDZV9nZW5lSUQsIHBlcmNlbnRfaG9tb2xvZ3ksIC5hZnRlciA9IFBDMl9pZCkKCiMgTWFrZSBJbnRlcmFjdGl2ZSBQbG90Cm15c2NvcmVzLlRvcDEwLmludGVyYWN0aXZlIDwtIG15c2NvcmVzLlRvcDEwICU+JQogIERUOjpkYXRhdGFibGUoZXh0ZW5zaW9ucyA9IGMoJ0tleVRhYmxlJywgIkZpeGVkSGVhZGVyIiksCiAgICAgICAgICAgICAgICByb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKAogICAgICAgICAgICAgICAgICBzdHlsZSA9ICdjYXB0aW9uLXNpZGU6IHRvcDsgdGV4dC1hbGlnbjogbGVmdDsnLAogICAgICAgICAgICAgICAgICBodG1sdG9vbHM6OnRhZ3MkYignVG9wIDEwJSBvZiBHZW5lcyBDb250cmlidXRpbmcgdG8gUEMxIGFuZCBQQzInKSwKICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGJyKCksCiAgICAgICAgICAgICAgICAgICdTZWFyY2ggZm9yIFBDMV9pZC9QQzJfaWQgdmFsdWVzIHRvIGZpbHRlciByZXN1bHRzJyksCiAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdChrZXlzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF1dG9XaWR0aCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxYID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyID0gbGlzdCgxLCAnZGVzYycpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VhcmNoSGlnaGxpZ2h0ID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdlTGVuZ3RoID0gMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoTWVudSA9IGMoIjEwIiwgIjI1IiwgIjUwIiwgIjEwMCIpKSkgJT4lCiAgRFQ6OmZvcm1hdFJvdW5kKGNvbHVtbnM9YygyOjMpLCBkaWdpdHM9MykKCm15c2NvcmVzLlRvcDEwLmludGVyYWN0aXZlCmBgYAoKCiMgRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIEdlbmVzIC0tLQpgYGB7ciBERUcsIG1lc3NhZ2U9RkFMU0V9CiMgSW50cm9kdWN0aW9uIHRvIHRoaXMgY2h1bmsgLS0tLQojIFRoaXMgY2h1bmsgdXNlcyBhIERHRUxpc3Qgb2YgZmlsdGVyZWQgYW5kIG5vcm1hbGl6ZWQgYWJ1bmRhbmNlIGRhdGEKIyBJdCB3aWxsIGZpdCBkYXRhIHRvIGEgbGluZWFyIG1vZGVsIGZvciByZXNwb25zaXZlbHkgZGV0ZWN0aW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyAoREVHcykKIyAKIyBUaGlzIGNodW5rIGRvZXMgYSBjb21wdXRhdGlvbmFsbHkgaW50ZXNuaXZlIHRoaW5nOiBjb25kdWN0ZXMgcGFpcndpc2UgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzZXMgZm9yIGV2ZXJ5IGdlbmUgYWNyb3NzIGV2ZXJ5IGNvbWJpbnRhdGlvbiBvZiBsaWZlIHN0YWdlcy4KIyAKIyBCZWNhdXNlIHdlIGhhdmUgYWNjZXNzIHRvIGJpb2xvZ2ljYWwgcmVwbGljYXRlcywgd2UgY2FuIHVzZSBzdGF0aXN0aWNhbCB0b29scyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKIyBVc2VmdWwgcmVhZGluZyBvbiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbjogaHR0cHM6Ly91Y2RhdmlzLWJpb2luZm9ybWF0aWNzLXRyYWluaW5nLmdpdGh1Yi5pby8yMDE4LUp1bmUtUk5BLVNlcS1Xb3Jrc2hvcC90aHVyc2RheS9ERS5odG1sCgojIExvYWQgcGFja2FnZXMgLS0tLQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkobGltbWEpICMgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiB1c2luZyBsaW5lYXIgbW9kZWxpbmcKICBsaWJyYXJ5KGVkZ2VSKQogIGxpYnJhcnkoZ3QpIAogIGxpYnJhcnkoRFQpIAogIGxpYnJhcnkocGxvdGx5KSAKfSkKCiMgU2V0IHVwIHRoZSBkZXNpZ24gbWF0cml4IC0tLS0KZ3JvdXAgPC0gZmFjdG9yKHRhcmdldHMkZ3JvdXApCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofjAgKyBncm91cCkgIyBubyBpbnRlcmNlcHQvYmxvY2tpbmcgZm9yIG1hdHJpeCwgY29tcGFyaXNvbnMgYWNyb3NzIGdyb3VwCmNvbG5hbWVzKGRlc2lnbikgPC0gbGV2ZWxzKGdyb3VwKQoKIyBOT1RFOiBUbyBoYW5kbGUgYSAnYmxvY2tpbmcnIGRlc2lnbicgb3IgYSBiYXRjaCBlZmZlY3QsIHVzZToKIyBkZXNpZ24gPC0gbW9kZWwubWF0cml4KH5ibG9jayArIHRyZWF0bWVudCkKCiMgTW9kZWwgbWVhbi12YXJpYW5jZSB0cmVuZCBhbmQgZml0IGxpbmVhciBtb2RlbCB0byBkYXRhIC0tLS0KIyBVc2UgVk9PTSBmdW5jdGlvbiBmcm9tIExpbW1hIHBhY2thZ2UgdG8gbW9kZWwgdGhlIG1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwCiMgcHJvZHVjZXMgYSB2YXJpYW5jZS1zdGFiaWxpemVkIERFR0xpc3QsIHRoYXQgaW5jbHVkZSBwcmVjaXNpb24gd2VpZ2h0cyBmb3IgZWFjaCBnZW5lIHRvIHRyeSBhbmQgY29udHJvbCBmb3IgaGV0ZXJvc2NlZGFzaXR5LgojIHRyYW5zZm9ybXMgY291bnQgZGF0YSB0byBsb2cyLWNvdW50cyBwZXIgbWlsbGlvbgojIE91dHB1dHM6IEUgPSBub3JtYWxpemVkIGV4cHJlc3Npb24gdmFsdWVzIG9uIHRoZSBsb2cyIHNjYWxlCnYuREVHTGlzdC5maWx0ZXJlZC5ub3JtIDwtIHZvb20oY291bnRzID0gbXlER0VMaXN0LmZpbHRlcmVkLm5vcm0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IGRlc2lnbiwgcGxvdCA9IEYpCmNvbG5hbWVzKHYuREVHTGlzdC5maWx0ZXJlZC5ub3JtKTwtdGFyZ2V0cyRzYW1wbGUKY29sbmFtZXModi5ERUdMaXN0LmZpbHRlcmVkLm5vcm0kRSkgPC0gcGFzdGUodGFyZ2V0cyRncm91cCwgdGFyZ2V0cyRzYW1wbGUsc2VwID0gJy0nKQoKIyBDYWxjdWxhdGUgYXZlcmFnZSBub3JtYWxpemVkIGxvZzItY21wIGFjcm9zcyBsaWZlIHN0YWdlcyAtLS0tCmF2Zy52LmZpbHRlcmVkLm5vcm0uTG9nMkNQTTwtdi5ERUdMaXN0LmZpbHRlcmVkLm5vcm0kRSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIiklPiUKICBzZXROYW1lcyhubSA9IGMoImdlbmVJRCIsIHRhcmdldHMkZ3JvdXApKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1nZW5lSUQsCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImxpZmVfc3RhZ2UiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImxvZzJDUE0iKSAlPiUKICBncm91cF9ieShnZW5lSUQsIGxpZmVfc3RhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpemUobGlmZS5zdGFnZS5BVkcgPSBtZWFuKGxvZzJDUE0sIG5hLnJtID0gVFJVRSkpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBsaWZlX3N0YWdlLAogICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJhdmdfIiwKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IGxpZmUuc3RhZ2UuQVZHLCkgJT4lCiAgdW5ncm91cCgpIAoKIyBTZXQgRXhwcmVzc2lvbiB0aHJlc2hvbGQgdmFsdWVzIGZvciBwbG90dGluZyBhbmQgc2F2aW5nIERFR3MgLS0tLQphZGouUC50aHJlc2ggPC0gMC4wNQpsZmMudGhyZXNoIDwtIDEKCiMgRml0IGEgbGluZWFyIG1vZGVsIHRvIHRoZSBkYXRhIC0tLS0KZml0IDwtIGxtRml0KHYuREVHTGlzdC5maWx0ZXJlZC5ub3JtLCBkZXNpZ24pCgojIEdlbmVyYXRlIGNvbXBhcmlzb24gbWF0cml4IGZvciBBTEwgdGhlIHBhaXJ3aXNlIGNvbXBhcmlzb25zIC0tLS0KY29tcGFyaXNvbnMgPC0gY3Jvc3NpbmcodGFyZ2V0U3RhZ2VzID0gdGFyZ2V0cyRncm91cCwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0U3RhZ2VzID0gdGFyZ2V0cyRncm91cCkgJT4lCiAgZHBseXI6OmZpbHRlcighKHRhcmdldFN0YWdlcyA9PSBjb250cmFzdFN0YWdlcykpCgpwYXN0ZS5jb21wYXJlIDwtIGZ1bmN0aW9uKHRhcmdldFN0YWdlLCBjb250cmFzdFN0YWdlKSB7CiAgcGFzdGUodGFyZ2V0U3RhZ2UsIGNvbnRyYXN0U3RhZ2UsIHNlcCA9ICItIikKfQpjb250cmFzdHMgPC0gbWFwcGx5KHBhc3RlLmNvbXBhcmUsIGNvbXBhcmlzb25zJHRhcmdldFN0YWdlcywgY29tcGFyaXNvbnMkY29udHJhc3RTdGFnZXMsIFVTRS5OQU1FUyA9IEYpCmNvbnRyYXN0Lm1hdHJpeCA8LSBtYWtlQ29udHJhc3RzKGNvbnRyYXN0cyA9IGNvbnRyYXN0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWRlc2lnbikKCiMgZXh0cmFjdCB0aGUgbGluZWFyIG1vZGVsIGZpdCAtLS0tLQpmaXRzIDwtIGNvbnRyYXN0cy5maXQoZml0LCBjb250cmFzdC5tYXRyaXgpCiMgZW1waXJpY2FsIGJheWVzIHNtb290aGluZyBvZiBnZW5lLXdpc2Ugc3RhbmRhcmQgZGV2aWF0aW9ucyBwcm92aWRlcyBpbmNyZWFzZWQgcG93ZXIgKHNlZTogaHR0cHM6Ly93d3cuZGVncnV5dGVyLmNvbS9kb2kvMTAuMjIwMi8xNTQ0LTYxMTUuMTAyNykKZWJGaXQgPC0gZUJheWVzKGZpdHMpCgojIFB1bGwgb3V0IHRoZSBERUdzIHRoYXQgcGFzcyBhIHNwZWNpZmljIHRocmVzaG9sZCBmb3IgYWxsIHBhaXJ3aXNlIGNvbXBhcmlzb25zIC0tLS0KcmVzdWx0cyA8LSBkZWNpZGVUZXN0cyhlYkZpdCwgbWV0aG9kPSJnbG9iYWwiLCBhZGp1c3QubWV0aG9kPSJCSCIsIHAudmFsdWU9YWRqLlAudGhyZXNoLCBsZmM9bGZjLnRocmVzaCkKCgojIEZ1bmN0aW9uIHRoYXQgaWRlbnRpZmllcyB0b3AgREVHIGJldHdlZW4gYSBzcGVjaWZpYyBwYWlyd2lzZSBjb21wYXJpc29uIC0tLS0KaWRlbnRpZnkuREVHcyA8LSBmdW5jdGlvbiAoc2VsZWN0ZWQuY29udHJhc3QsIGNvbnRyYXN0cykgewogICMgVG9wVGFibGUgdG8gdmlldyBERUdzIC0tLS0tCiAgIyBMb29rIGF0IHRoZSB0b3AgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzCiAgIyBjb2VmID0gd2hpY2ggY29udHJhc3QgdG8gZXhhbWluZQogICMgYWRqdXN0ID0gaG93IHRvIGFkanVzdCBwIHZhbHVlcyBmb3IgbXVsdGlwbGUgY29tcGFyaXNvbnMuIAogICMgSGVyZSB1c2luZyBCZW5qYW1pbmktSG9jaGJlcmcgZmFsc2UgZGlzY292ZXJ5IHJhdGUgYWRqdXN0ZWQgcC12YWx1ZS4KICAjIAogICMgVG9wVGFibGUgKGZyb20gTGltbWEpIG91dHB1dHMgYSBmZXcgZGlmZmVyZW50IHN0YXRzOgogICMgbG9nRkMsIEF2ZUV4cHIsIGFuZCBQLlZhbHVlIHNob3VsZCBiZSBzZWxmLWV4cGxhbmF0b3J5LCBleGNlcHQgZm9yIG5vdGluZyB0aGF0IEF2ZUV4cHIgaXMgdGhlIGxvZzItZXhwcmVzc2lvbiBhY3Jvc3MgYWxsIGdyb3VwcwogICMgYWRqLlAuVmFsIGlzIHlvdXIgYWRqdXN0ZWQgUCB2YWx1ZSwgYWxzbyBrbm93biBhcyBhbiBGRFIgKGlmIEJIIG1ldGhvZCB3YXMgdXNlZCBmb3IgbXVsdGlwbGUgdGVzdGluZyBjb3JyZWN0aW9uKQogICMgQiBzdGF0aXN0aWMgaXMgdGhlIGxvZy1vZGRzIHRoYXQgdGhhdCBnZW5lIGlzIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZC4gSWYgQiA9IDEuNSwgdGhlbiBsb2cgb2RkcyBpcyBlXjEuNSwgd2hlcmUgZSBpcyBldWxlcidzIGNvbnN0YW50IChhcHByb3guIDIuNzE4KS4gIFNvLCB0aGUgb2RkcyBvZiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvcyBhYm91dCA0LjggdG8gMSAKICAjIHQgc3RhdGlzdGljIGlzIHJhdGlvIG9mIHRoZSBsb2dGQyB0byB0aGUgc3RhbmRhcmQgZXJyb3IgKHdoZXJlIHRoZSBlcnJvciBoYXMgYmVlbiBtb2RlcmF0ZWQgYWNyb3NzIGFsbCBnZW5lcy4uLmJlY2F1c2Ugb2YgQmF5ZXNpYW4gYXBwcm9hY2gpCiAgIyBwYXJzZSBzZWxlY3RlZCBjb250cmFzdCBpbnRvIHRoZSBjb2VmZmljaWVudAogIGNvZWYgPC0gZ3JlcChwYXN0ZTAoIl4iLHNlbGVjdGVkLmNvbnRyYXN0LCIkIiksIGNvbnRyYXN0cykKICB0YXJnZXRTdGFnZSA8LSBzdHJfc3BsaXQoc2VsZWN0ZWQuY29udHJhc3QsICItIilbWzFdXVsxXQogIGNvbnRyYXN0U3RhZ2UgPC0gc3RyX3NwbGl0KHNlbGVjdGVkLmNvbnRyYXN0LCAiLSIpW1sxXV1bMl0KICAKICBteVRvcEhpdHMuZGYgPC0gbGltbWE6OnRvcFRhYmxlKGViRml0LCBhZGp1c3QgPSJCSCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZj1jb2VmLCBudW1iZXI9NDAwMDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ydC5ieT0ibG9nRkMiKSAlPiUKICAgIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lSUQiKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUodFN0YXRpc3RpYyA9IHQsIExvZ09kZHMgPSBCLCBCSC5hZGouUC5WYWwgPSBhZGouUC5WYWwpICU+JQogICAgZHBseXI6OnJlbG9jYXRlKFVuaVByb3RLQiwgRGVzY3JpcHRpb24sIEludGVyUHJvLCBHT190ZXJtLCBDZV9nZW5lSUQsIHBlcmNlbnRfaG9tb2xvZ3ksIC5hZnRlciA9IExvZ09kZHMpCiAgCiAgIyBWb2xjYW5vIFBsb3RzIC0tLS0KICB2cGxvdCA8LSBnZ3Bsb3QobXlUb3BIaXRzLmRmKSArCiAgICBhZXMoeT0tbG9nMTAoQkguYWRqLlAuVmFsKSwgeD1sb2dGQywgdGV4dCA9IHBhc3RlKGdlbmVJRCwgIjxicj4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9nRkM6Iiwgcm91bmQobG9nRkMsIGRpZ2l0cyA9IDIpLCAiPGJyPiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwLXZhbDoiLCBmb3JtYXQoQkguYWRqLlAuVmFsLCBkaWdpdHMgPSAzLCBzY2llbnRpZmljID0gVFJVRSksICI8YnI+IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlc2NyaXB0aW9uOiIsIERlc2NyaXB0aW9uLCAiPGJyPiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSW50ZXJQcm86IiwgSW50ZXJQcm8sIjxicj4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2UtR2VuZToiLCBDZV9nZW5lSUQsICI8YnI+IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIlIGhvbW9sb2d5OiIsIHBlcmNlbnRfaG9tb2xvZ3kpKSArCiAgICBnZW9tX3BvaW50KHNpemU9MikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKGFkai5QLnRocmVzaCksIGxpbmV0eXBlPSJsb25nZGFzaCIsIGNvbG91cj0iZ3JleSIsIHNpemU9MSkgKyAjZGFzaGVkIGxpbmUgYXQgZ2l2ZW4gcCB2YWx1ZSwgdy86IC1sb2cxMChwKQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbGZjLnRocmVzaCwgbGluZXR5cGU9ImxvbmdkYXNoIiwgY29sb3VyPSIjQkU2ODREIiwgc2l6ZT0xKSArICNsb2dGQyBvZiAxID0gMiBmb2xkIGNoYW5nZQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gLWxmYy50aHJlc2gsIGxpbmV0eXBlPSJsb25nZGFzaCIsIGNvbG91cj0iIzJDNDY3QSIsIHNpemU9MSkgKwogICAgbGFicyh0aXRsZT0iVm9sY2FubyBwbG90IiwKICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIlMuIHN0ZXJjb3JhbGlzICIsIHNlbGVjdGVkLmNvbnRyYXN0KSwKICAgICAgICAgY2FwdGlvbj1wYXN0ZTAoInByb2R1Y2VkIG9uICIsIFN5cy50aW1lKCkpKSArCiAgICB0aGVtZV9idygpCiAgCiAgIyBOb3cgbWFrZSB0aGUgdm9sY2FubyBwbG90IGFib3ZlIGludGVyYWN0aXZlIHdpdGggcGxvdGx5CiAgaW50ZXJhY3RpdmUudnBsb3QgPC0gZ2dwbG90bHkodnBsb3QsIHRvb2x0aXAgPSAidGV4dCIpICU+JQogICAgbGF5b3V0KHRpdGxlPSBsaXN0KCB0ZXh0ID0gcGFzdGUwKCJJbnRlcmFjdGl2ZSBWb2xjYW5vIHBsb3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHN1cD4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTLiBzdGVyY29yYWxpcyAiLCBzZWxlY3RlZC5jb250cmFzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPC9zdXA+IikpKQogIAogICMgR2VuZXJhdGUgdmFyaWFibGUgY29udGFpbmluZyBleHByZXNzaW9uIGRhdGEgZm9yIHlvdXIgdGhyZXNob2xkZWQgREVHcyAtLS0tCiAgIyBSZW1pbmRlciB0aGF0IEUgKG91dHB1dCBmcm9tIHZvb20pID0gbm9ybWFsaXplZCBleHByZXNzaW9uIHZhbHVlcyBvbiB0aGUgbG9nMiBzY2FsZQogIGRpZmZHZW5lcyA8LSB2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSRFW3Jlc3VsdHNbLGNvZWZdICE9MCxdICMgWyxjb2VmXSBzcGVjaWZpZXMgd2hpY2ggcGFpcndpc2UgY29tcGFyaXNvbiB0byBwdWxsIG91dC4gVGhpcyB2YXJpYWJsZSBpcyBuZWNlc3NhcnkgZm9yIGdlbmVyYXRpbmcgaGVhdG1hcHMuCiAgZGlmZkdlbmVzLmRmIDwtIGFzX3RpYmJsZShkaWZmR2VuZXMsIHJvd25hbWVzID0gImdlbmVJRCIsIC5uYW1lX3JlcGFpciA9ICJ1bmlxdWUiKSAlPiUgIyBjb252ZXJ0IERFR3MgdG8gYSBkYXRhZnJhbWUKICAgIGxlZnRfam9pbiguLCBhdmcudi5maWx0ZXJlZC5ub3JtLkxvZzJDUE0sIGJ5ID0gImdlbmVJRCIpICAjIGFkZCBhdmVyYWdlZCBsaWZlIHN0YWdlIGV4cHJlc3Npb24gaW5mb3JtYXRpb24gdG8gdGhlIGRhdGFmcmFtZQogIGRpZmZHZW5lcy5Ub3BIaXRzIDwtIGxlZnRfam9pbihkaWZmR2VuZXMuZGYsIG15VG9wSGl0cy5kZiwgYnkgPSAiZ2VuZUlEIikgIyBtZXJnZSBsaXN0IG9mIHRocmVzaG9sZGVkIERFR3Mgd2l0aCBpbmZvcm1hdGlvbiBjb250YWluZWQgaW4gVG9wVGFibGUKICBkaWZmR2VuZXMuVG9wSGl0cyRCSC5hZGouUC5WYWwgPC0gZm9ybWF0QyhkaWZmR2VuZXMuVG9wSGl0cyRCSC5hZGouUC5WYWwsIGRpZ2l0cyA9IDMsIGZvcm1hdCA9ICJFIikgIyBTZXQgYWRqdXN0ZWQgUCB2YWx1ZSB0byBzY2llbnRpZmljIG5vdGF0aW9uIGZvciBjb3NtZXRpYyBwdXJwb3NlcwogIAogICMgY3JlYXRlIGludGVyYWN0aXZlIHRhYmxlcyB0byBkaXNwbGF5IHRoZSB0aHJlc2hvbGRlZCBERUdzLCByYW5rZWQgLS0tLQogIGlmKGFueShncmVwbCh0YXJnZXRTdGFnZSwgbmFtZXMoZGlmZkdlbmVzLlRvcEhpdHMpKSkpewogICAgREVHLmRhdGF0YWJsZSA8LSBkaWZmR2VuZXMuVG9wSGl0cyAlPiUKICAgICAgZHBseXI6OnNlbGVjdChnZW5lSUQsIHN0YXJ0c193aXRoKHBhc3RlMCh0YXJnZXRTdGFnZSwiLSIpKSwgCiAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJhdmdfIix0YXJnZXRTdGFnZSksCiAgICAgICAgICAgICAgICAgICAgc3RhcnRzX3dpdGgocGFzdGUwKGNvbnRyYXN0U3RhZ2UsIi0iKSksCiAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJhdmdfIiwgY29udHJhc3RTdGFnZSksCiAgICAgICAgICAgICAgICAgICAgbG9nRkMsIEJILmFkai5QLlZhbDpwZXJjZW50X2hvbW9sb2d5KX0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICBERUcuZGF0YXRhYmxlIDwtIGRpZmZHZW5lcy5Ub3BIaXRzICU+JQogICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KGdlbmVJRCwgc3RhcnRzX3dpdGgoImF2ZyIpLGxvZ0ZDLCBCSC5hZGouUC5WYWw6cGVyY2VudF9ob21vbG9neSkKICAgICAgICAgICAgICAgICAgICB9CiAgREVHLmRhdGF0YWJsZSA8LSBERUcuZGF0YXRhYmxlICU+JQogICAgRFQ6OmRhdGF0YWJsZShleHRlbnNpb25zID0gYygnS2V5VGFibGUnLCAiRml4ZWRIZWFkZXIiKSwKICAgICAgICAgICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKAogICAgICAgICAgICAgICAgICAgIHN0eWxlID0gJ2NhcHRpb24tc2lkZTogdG9wOyB0ZXh0LWFsaWduOiBsZWZ0OycsCiAgICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGIoJ0RpZmZlcmVudGlhbGx5IEV4cHJlc3NlZCBHZW5lcyBpbicsIGh0bWx0b29sczo6dGFncyRlbSgnUy4gc3RlcmNvcmFsaXMnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0U3RhZ2UsICcgdnMgJywgY29udHJhc3RTdGFnZSksCiAgICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGJyKCksCiAgICAgICAgICAgICAgICAgICAgIlRocmVzaG9sZDogcCA8ICIsCiAgICAgICAgICAgICAgICAgICAgYWRqLlAudGhyZXNoLCAiOyBsb2ctZm9sZCBjaGFuZ2UgPiAiLAogICAgICAgICAgICAgICAgICAgIGxmYy50aHJlc2gsCiAgICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGJyKCksCiAgICAgICAgICAgICAgICAgICAgJ1ZhbHVlcyA9IGxvZzIgY291bnRzIHBlciBtaWxsaW9uJyksCiAgICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGtleXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdXRvV2lkdGggPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxYID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBsaXN0KDksICdkZXNjJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlYXJjaEhpZ2hsaWdodCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdlTGVuZ3RoID0gMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGhNZW51ID0gYygiMTAiLCAiMjUiLCAiNTAiLCAiMTAwIikpKQogIAogIGlmKGFueShncmVwbCh0YXJnZXRTdGFnZSwgbmFtZXMoZGlmZkdlbmVzLlRvcEhpdHMpKSkpewogICAgREVHLmRhdGF0YWJsZSA8LSBERUcuZGF0YXRhYmxlICU+JQogICAgICBEVDo6Zm9ybWF0Um91bmQoY29sdW1ucz1jKDI6OSksIGRpZ2l0cz0yKSAlPiUKICAgICAgRFQ6OmZvcm1hdFJvdW5kKGNvbHVtbnMgPSBjKDEwLDEyKSwgZGlnaXRzID0gMyl9IGVsc2UgewogICAgICAgIERFRy5kYXRhdGFibGUgPC0gREVHLmRhdGF0YWJsZSAlPiUKICAgICAgICAgIERUOjpmb3JtYXRSb3VuZChjb2x1bW5zPWMoMjo4KSwgZGlnaXRzPTIpICU+JQogICAgICAgICAgRFQ6OmZvcm1hdFJvdW5kKGNvbHVtbnMgPSBjKDksMTEpLCBkaWdpdHMgPSAzKQogICAgICB9IAogIAogIG91dHB1dCA8LSBsaXN0KG15VG9wSGl0cy5kZiA9IG15VG9wSGl0cy5kZiwgCiAgICAgICAgICAgICAgICAgZGlmZkdlbmVzID0gZGlmZkdlbmVzLAogICAgICAgICAgICAgICAgIHZwbG90ID0gdnBsb3QsIAogICAgICAgICAgICAgICAgIGludGVyYWN0aXZlLnZwbG90ID0gaW50ZXJhY3RpdmUudnBsb3QsIAogICAgICAgICAgICAgICAgIGRpZmZHZW5lcy5Ub3BIaXRzID0gZGlmZkdlbmVzLlRvcEhpdHMsCiAgICAgICAgICAgICAgICAgREVHLmRhdGF0YWJsZSA9IERFRy5kYXRhdGFibGUpCn0KCiMgQ2FsbGluZyBmdW5jdGlvbiBvbiBhbGwgcGFpcndpc2UgY29tcGFyaXNvbnMgLS0tLS0Kb3V0cHV0LkRFRy5Tc1JOQXNlcSA8LSBzYXBwbHkoY29udHJhc3RzLCBpZGVudGlmeS5ERUdzLCBjb250cmFzdHMsIHNpbXBsaWZ5ID0gRiwgVVNFLk5BTUVTID0gVCkKbmFtZXMob3V0cHV0LkRFRy5Tc1JOQXNlcSkgPC0gIHBhc3RlKGNvbXBhcmlzb25zJHRhcmdldFN0YWdlcywgY29tcGFyaXNvbnMkY29udHJhc3RTdGFnZXMsIHNlcCA9ICItIikKCiMgQ2FjaGluZyB0aGUgY2FsbCB0byBpZGVudGlmeS5ERUdzIGIvYyBpdCdzIGNvbXB1dGF0aW9uYWxseSBleHBlbnNpdmUuICAtLS0tCiNpZiAoIWlzLm1lbW9pc2VkKG91dHB1dC5ERUcuU3NSTkFzZXEpKSB7CiMgb3V0cHV0LkRFRy5Tc1JOQXNlcSA8LSBtZW1vaXNlKG91dHB1dC5ERUcuU3NSTkFzZXEpfQojICMgUmVtb3ZlIENhY2hlIC0gaWYgSSBtYWRlIGEgY2hhbmdlIHRvIHRoZSB1bmRlcmx5aW5nIGZ1bmN0aW9uCiMgZm9yZ2V0KHBhaXJ3aXNlLkNvbXBhcmUpCgpgYGAKCiMgQWNyb3NzIEFsbCBMaWZlIFN0YWdlczogREVHcyAtLS0KYGBge3IgYWNyb3NzR3JvdXBzREVHLCB3YXJuaW5nPUZBTFNFfQojIEludHJvZHVjdGlvbiB0byB0aGlzIGNodW5rIC0tLS0KIyBUaGlzIGNodW5rIGNvbWJpbmVzIGFsbCBwYWlyLXdpc2UgY29tcGFyaXNvbnMgdG8gaWRlbnRpeSBhbGwgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIGFueSB3YXkgYWNyb3NzIGdyb3VwcwoKIyBWYXJpYXRpb24gb2YgdGhlIHRvcFRhYmxlIGNhbGwgdG8gdXNlIGFsbCBjb21wYXJpc29ucyAtLS0tCiMgVGhpcyBpcyBkb25lIGJ5IG5vdCBpbmNsdWRpbmcgYSBjb2VmZmljaWVudCB2YXJpYWJsZS4KIyBmcm9tIHRoZSByZXN1bHRzIG9mIHRoZSBlQmF5ZXMgZnVuY3Rpb24sIHRoZSBzdGF0aXN0aWMgJEYgYW5kIHRoZSAkRi5wLnZhbHVlIGNvbWJpbmUgYWxsIHBhaXJ3aXNlIGNvbXBhcmlzb25zIGludG8gb25lIEYtdGVzdC4gU28gdGhpcyBzaG91bGQgaW5kaXZjYXRlIHdoaWNoIGdlbmVzIHZhcnkgYmV0d2VlbiB0aGUgdGFyZ2V0cyBpbiBhbnkgd2F5LgojIApteVRvcEhpdHMuYWNyb3NzLmRmIDwtIGxpbW1hOjp0b3BUYWJsZShlYkZpdCwgYWRqdXN0ID0iQkgiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyPTQwMDAwKSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIikgJT4lCiAgZHBseXI6OnJlbmFtZShmU3RhdGlzdGljID0gRiwgQkguYWRqLlAuVmFsID0gYWRqLlAuVmFsKSAlPiUKICBkcGx5cjo6cmVsb2NhdGUoVW5pUHJvdEtCLCBEZXNjcmlwdGlvbiwgSW50ZXJQcm8sIEdPX3Rlcm0sIENlX2dlbmVJRCwgcGVyY2VudF9ob21vbG9neSwgLmFmdGVyID0gQkguYWRqLlAuVmFsKQoKCiMgUHVsbCBvdXQgdGhlIERFR3MgdGhhdCBwYXNzIGEgc3BlY2lmaWMgdGhyZXNob2xkIGZvciBhbGwgcGFpcndpc2UgY29tcGFyaXNvbnMgLS0tLQojIHAudmFsdWUgPSBzZXQgcCB2YWx1ZSB0aHJlc2hvbGQsIGxmYyA9IHNldCBsb2cgZm9sZCBjaGFuZ2UgdGhyZXNob2xkLCAxID0gZm9sZCBjaGFuZ2Ugb2YgMgpyZXN1bHRzIDwtIGRlY2lkZVRlc3RzKGViRml0LCBtZXRob2Q9Imdsb2JhbCIsIGFkanVzdC5tZXRob2Q9IkJIIiwgcC52YWx1ZT1hZGouUC50aHJlc2gsIGxmYz1sZmMudGhyZXNoKQoKIyBHZW5lcmF0ZSB2YXJpYWJsZSBjb250YWluaW5nIGV4cHJlc3Npb24gZGF0YSBmb3IgeW91ciB0aHJlc2hvbGRlZCBERUdzIC0tLS0KZGlmZkdlbmVzLmFjcm9zcyA8LSB2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSRFW3Jvd1N1bXMocmVzdWx0cyAhPTApID4gMCxdICMgYWNyb3NzIGFsbCBjb21wYXJpc29ucyAKY29sbmFtZXMoZGlmZkdlbmVzLmFjcm9zcykgPC0gdGFyZ2V0cyRncm91cAoKZGlmZkdlbmVzLlRvcEhpdHMuYWNyb3NzIDwtYXZlYXJyYXlzKGRpZmZHZW5lcy5hY3Jvc3MpICU+JQogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lSUQiKSAlPiUgCiAgbGVmdF9qb2luKC4sIAogICAgICAgICAgICBkcGx5cjo6c2VsZWN0KG15VG9wSGl0cy5hY3Jvc3MuZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZUlELGZTdGF0aXN0aWMsIAogICAgICAgICAgICAgICAgICAgICAgICAgIEJILmFkai5QLlZhbDpwZXJjZW50X2hvbW9sb2d5KSwgCiAgICAgICAgICAgIGJ5ID0gImdlbmVJRCIpCmRpZmZHZW5lcy5Ub3BIaXRzLmFjcm9zcyRCSC5hZGouUC5WYWwgPC0gZm9ybWF0QyhkaWZmR2VuZXMuVG9wSGl0cy5hY3Jvc3MkQkguYWRqLlAuVmFsLCBkaWdpdHMgPSAzLCBmb3JtYXQgPSAiRSIpICMgU2V0IGFkanVzdGVkIFAgdmFsdWUgdG8gc2NpZW50aWZpYyAKCgojIE1ha2UgdGhlIHRhYmxlIGludGVyYWN0aXZlPyAtLS0tCiMgTm90ZSB0aGF0IHRoaXMgdGFibGUgd29uJ3QgdGVsbCB5b3Ugd2hpY2ggY29tcGFyaXNvbnMgYXJlIGRyaXZpbmcgdGhlIHNpZ25pZmljYW50IGRpZmZlcmVuY2UsIG9ubHkgdGhhdCB0aGV5IGRvIHZhcnkuCmFsbC5ERUcuZGF0YXRhYmxlIDwtIGRpZmZHZW5lcy5Ub3BIaXRzLmFjcm9zcyAlPiUKICBEVDo6ZGF0YXRhYmxlKGV4dGVuc2lvbnMgPSBjKCdLZXlUYWJsZScsICJGaXhlZEhlYWRlciIpLAogICAgICAgICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbigKICAgICAgICAgICAgICAgICAgc3R5bGUgPSAnY2FwdGlvbi1zaWRlOiB0b3A7IHRleHQtYWxpZ246IGxlZnQ7JywKICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGIoJ0RpZmZlcmVudGlhbGx5IEV4cHJlc3NlZCBHZW5lcyBpbicsIGh0bWx0b29sczo6dGFncyRlbSgnUy4gc3RlcmNvcmFsaXMnKSksCiAgICAgICAgICAgICAgICAgIGh0bWx0b29sczo6dGFncyRicigpLAogICAgICAgICAgICAgICAgICAiR2VuZXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIGFueSBsaWZlIHN0YWdlIiwKICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGJyKCksCiAgICAgICAgICAgICAgICAgICJUaHJlc2hvbGQ6IHAgPCAiLAogICAgICAgICAgICAgICAgICBhZGouUC50aHJlc2gsICI7IGxvZy1mb2xkIGNoYW5nZSA+ICIsCiAgICAgICAgICAgICAgICAgIGxmYy50aHJlc2gsCiAgICAgICAgICAgICAgICAgIGh0bWx0b29sczo6dGFncyRicigpLAogICAgICAgICAgICAgICAgICAnVmFsdWVzID0gYXZlcmFnZSBsb2cyIGNvdW50cyBwZXIgbWlsbGlvbicpLAogICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3Qoa2V5cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdXRvV2lkdGggPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciA9IGxpc3QoOSwgJ2FzYycpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VhcmNoSGlnaGxpZ2h0ID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdlTGVuZ3RoID0gMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoTWVudSA9IGMoIjEwIiwgIjI1IiwgIjUwIiwgIjEwMCIpKSkgJT4lCiAgRFQ6OmZvcm1hdFJvdW5kKGNvbHVtbnM9YygyOjgpLCBkaWdpdHM9MikgJT4lCiAgRFQ6OmZvcm1hdFJvdW5kKGNvbHVtbnMgPSBjKDkpLCBkaWdpdHMgPSAzKSAKCmFsbC5ERUcuZGF0YXRhYmxlCmBgYAoKIyBDbHVzdGVyIERFR3MgaW50byBmdW5jdGlvbmFsIG1vZHVsZXMgLS0tCmBgYHtyIGhlYXRtYXBzfQojIEludHJvZHVjdGlvbiB0byB0aGlzIGNodW5rIC0tLS0KIyB0aGlzIGNodW5rIGNyZWF0ZXMgaGVhdG1hcHMgZnJvbSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXM7CiMgaXQgdGFrZXMgYXMgaW5wdXQgYSBsaXN0IG9mIGdlbmVzIHRoYXQgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiBhbnkgbGlmZSBzdGFnZQojIEl0IHNlbGVjdHMgbW9kdWxlcyBvZiBjby1leHByZXNzZWQgZ2VuZXMgYmFzZWQgb24gcGVhcnNvbiBjb3JyZWxhdGlvbnMKCiMgTG9hZCBwYWNrYWdlcyAtLS0tLQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkobGltbWEpCiAgbGlicmFyeShSQ29sb3JCcmV3ZXIpCiAgbGlicmFyeShncGxvdHMpCiAgbGlicmFyeShoZWF0bWFwbHkpCn0pCgojIENob29zZSBhIGNvbG9yIHBhbGxldHRlIC0tLS0KI215aGVhdGNvbG9ycyA8LSByZXYoYnJld2VyLnBhbChuYW1lPSJSZEJ1Iiwgbj0xMSkpCm15aGVhdGNvbG9ycyA8LSBSZEJ1KDc1KQoKIyBDbHVzdGVyIERFR3MgYWNyb3NzIHN0YWdlcyAtLS0tCiNiZWdpbiBieSBjbHVzdGVyaW5nIHRoZSBnZW5lcyAocm93cykgZm9yIGEgbGlzdCBvZiBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gYXQgbGVhc3Qgb25lIGxpZmUgc3RhZ2UKIyB1c2UgdGhlICdjb3InIGZ1bmN0aW9uIGFuZCB0aGUgcGVhcnNvbiBtZXRob2QgZm9yIGZpbmRpbmcgYWxsIHBhaXJ3aXNlIGNvcnJlbGF0aW9ucyBvZiBnZW5lcwojICcxLWNvcicgY29udmVydHMgdGhpcyB0byBhIDAtMiBzY2FsZSBmb3IgZWFjaCBvZiB0aGVzZSBjb3JyZWxhdGlvbnMsIHdoaWNoIGNhbiB0aGVuIGJlIHVzZWQgdG8gY2FsY3VsYXRlIGEgZGlzdGFuY2UgbWF0cml4IHVzaW5nICdhcy5kaXN0JwpjbHVzdFJvd3MgPC0gaGNsdXN0KGFzLmRpc3QoMS1jb3IodChkaWZmR2VuZXMuYWNyb3NzKSwgbWV0aG9kPSJwZWFyc29uIikpLCBtZXRob2Q9ImNvbXBsZXRlIikgCiMgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgaXMgYSB0eXBlIG9mIHVuc3VwZXJ2aXNlZCBjbHVzdGVyaW5nLiAKIyBOT1RFOiB0aGlzIGNsdXN0ZXIgbWF5IHByb3ZpZGUgZGlmZmVyZW50IHJlc3VsdHMgdG8gb25lIGJhc2VkIG9uIGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0gZGF0YSwgbGlrZWx5IGIvYyB0aGlzIHZlcnNpb24gaXMgc3BlY2lmY2FsbHkgZm9jdXNlZCBvbiBnZW5lcyB0aGF0IGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBiZXR3ZWVuIGNvbmRpdGlvbnMuCiMgUmVsYXRlZCBtZXRob2RzIGluY2x1ZGUgSy1tZWFucywgU09NLCBldGMgCiMgdW5zdXBlcnZpc2VkIG1ldGhvZHMgYXJlIGJsaW5kIHRvIHNhbXBsZS9ncm91cCBpZGVudGl0eQojIGluIGNvbnRyYXN0LCBzdXBlcnZpc2VkIG1ldGhvZHMgJ3RyYWluJyBvbiBhIHNldCBvZiBsYWJlbGVkIGRhdGEuICAKIyBzdXBlcnZpc2VkIGNsdXN0ZXJpbmcgbWV0aG9kcyBpbmNsdWRlIHJhbmRvbSBmb3Jlc3QsIGFuZCBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrcwoKIyBjbHVzdGVyIHNhbXBsZXMgKGNvbHVtbnMpCmNsdXN0Q29sdW1ucyA8LSBoY2x1c3QoYXMuZGlzdCgxLWNvcihkaWZmR2VuZXMuYWNyb3NzLCBtZXRob2Q9InNwZWFybWFuIikpLCBtZXRob2Q9ImNvbXBsZXRlIikgI2NsdXN0ZXIgY29sdW1ucyBieSBzcGVhcm1hbiBjb3JyZWxhdGlvbgojbm90ZTogdXNlIFNwZWFybWFuLCBpbnN0ZWFkIG9mIFBlYXJzb24sIGZvciBjbHVzdGVyaW5nIHNhbXBsZXMgYmVjYXVzZSBpdCBnaXZlcyBlcXVhbCB3ZWlnaHQgdG8gaGlnaGx5IHZzIGxvd2x5IGV4cHJlc3NlZCB0cmFuc2NyaXB0cyBvciBnZW5lcwoKI0N1dCB0aGUgcmVzdWx0aW5nIHRyZWUgYW5kIGNyZWF0ZSBjb2xvciB2ZWN0b3IgZm9yIGNsdXN0ZXJzLiAgCm1vZHVsZS5hc3NpZ24gPC0gc3RhdHM6OmN1dHJlZShjbHVzdFJvd3MsIGs9MTQpICNUaGUgZGlmZkdlbmVzIGluZm8gaXMgYmFzZWQgb24gYSBwYWlyd2lzZSBjb21wYXJpc29uIGJldHdlZW4gYWxsIDcgbGlmZSBzdGFnZXMuIAoKIyBhc3NpZ24gYSBjb2xvciB0byBlYWNoIG1vZHVsZSAobWFrZXMgaXQgZWFzeSB0byBpZGVudGlmeSBhbmQgbWFuaXB1bGF0ZSkKbW9kdWxlLmNvbG9yIDwtIHJhaW5ib3cobGVuZ3RoKHVuaXF1ZShtb2R1bGUuYXNzaWduKSksIHN0YXJ0PTAuMSwgZW5kPTAuOSkgCm1vZHVsZS5jb2xvciA8LSBtb2R1bGUuY29sb3JbYXMudmVjdG9yKG1vZHVsZS5hc3NpZ24pXSAKCiMgc2ltcGxmeSBoZWF0bWFwIGJ5IGF2ZXJhZ2luZyB0aGUgYmlvbG9naWNhbCByZXBsaWNhdGVzIGFuZCBkaXNwbGF5IG9ubHkgb25lIGNvbHVtbiBwZXIgY29uZGl0aW9uCmNvbG5hbWVzKGRpZmZHZW5lcy5hY3Jvc3MpIDwtIHRhcmdldHMkZ3JvdXAKZGlmZkdlbmVzLkFWRyA8LSBhdmVhcnJheXMoZGlmZkdlbmVzLmFjcm9zcykKCiMgcGxvdCB0aGUgaGNsdXN0IHJlc3VsdHMgYXMgYSBoZWF0bWFwLCBncm91cGluZyB0aGUgbGlmZSBzdGFnZXMgdG9nZXRoZXIKZGlmZkdlbmVzLmhlYXRtYXAuYnlncm91cCA8LSBoZWF0bWFwLjIoZGlmZkdlbmVzLkFWRywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNydENvbCA9IDAsIGFkakNvbD0gYygwLjUsMC41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93dj1hcy5kZW5kcm9ncmFtKGNsdXN0Um93cyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleS50aXRsZSA9IE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWluID0gcGFzdGUwKCJERUcgSGVhdG1hcCAoYnkgbGlmZSBzdGFnZSk6ICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWIgPSBwYXN0ZTAoIkdlbmVzIHBhc3MgdGhyZXNob2xkIGluID49IDEgcGFpcndpc2UgY29tcGFyaXNvbi4gVGhyZXNob2xkOiBwIDwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFkai5QLnRocmVzaCwgIjsgbG9nLWZvbGQgY2hhbmdlID4gIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxmYy50aHJlc2gpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3dTaWRlQ29sb3JzPW1vZHVsZS5jb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPXJldihteWhlYXRjb2xvcnMpLCBzY2FsZT0ncm93JywgbGFiUm93PU5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW5zaXR5LmluZm89Im5vbmUiLCB0cmFjZT0ibm9uZSIsICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V4Um93PTEsIGNleENvbD0xKSAKCiMgUGFpcndpc2UgQ2x1c3RlcmluZy9IZWF0bWFwcwpwYWlyd2lzZS5DbHVzdGVyaW5nIDwtIGZ1bmN0aW9uICh0YXJnZXRTdGFnZSxjb250cmFzdFN0YWdlKXsKICAjR2VuZXJhdGUgbmFtZSBvZiBwYWlyd2lzZSBjb21wYXJpc29uCiAgeCA8LSBwYXN0ZSh0YXJnZXRTdGFnZSwgY29udHJhc3RTdGFnZSwgc2VwID0gIi0iKQogIHByaW50KHBhc3RlKCJDbHVzdGVyaW5nICIseCkpCiAgc3Vic2V0LmRpZmZHZW5lcyA8LSBvdXRwdXQuREVHLlNzUk5Bc2VxW1t4XV0kZGlmZkdlbmVzCiAgY29sbmFtZXMoc3Vic2V0LmRpZmZHZW5lcykgPC0gdGFyZ2V0cyRncm91cAogIHNlbGVjdGVkLmNvbHMgPC0gZ3JlcGwocGFzdGUwKCJeIiwgdGFyZ2V0U3RhZ2UsICIkIiksIGNvbG5hbWVzKHN1YnNldC5kaWZmR2VuZXMpKSB8IAogICAgZ3JlcGwocGFzdGUwKCJeIiwgY29udHJhc3RTdGFnZSwgIiQiKSwgY29sbmFtZXMoc3Vic2V0LmRpZmZHZW5lcykpCiAgc3Vic2V0LmRpZmZHZW5lcyA8LSBzdWJzZXQuZGlmZkdlbmVzWyxzZWxlY3RlZC5jb2xzXQogIAogIHN1YnNldC5jbHVzdFJvd3MgPC0gaGNsdXN0KGFzLmRpc3QoMS1jb3IodChzdWJzZXQuZGlmZkdlbmVzKSwgbWV0aG9kPSJwZWFyc29uIikpLCBtZXRob2Q9ImNvbXBsZXRlIikKICBzdWJzZXQuY2x1c3RDb2x1bW5zIDwtIGhjbHVzdChhcy5kaXN0KDEtY29yKHN1YnNldC5kaWZmR2VuZXMsIG1ldGhvZD0ic3BlYXJtYW4iKSksIG1ldGhvZD0iY29tcGxldGUiKSAKICBzdWJzZXQubW9kdWxlLmFzc2lnbiA8LSBzdGF0czo6Y3V0cmVlKHN1YnNldC5jbHVzdFJvd3MsIGs9MikKICBzdWJzZXQubW9kdWxlLmNvbG9yIDwtIHJhaW5ib3cobGVuZ3RoKHVuaXF1ZShzdWJzZXQubW9kdWxlLmFzc2lnbikpLCBzdGFydD0wLjEsIGVuZD0wLjkpIAogIHN1YnNldC5tb2R1bGUuY29sb3IgPC0gc3Vic2V0Lm1vZHVsZS5jb2xvclthcy52ZWN0b3Ioc3Vic2V0Lm1vZHVsZS5hc3NpZ24pXQogIAogIHN1YnNldC5kaWZmR2VuZXMuaGVhdG1hcC5ieWdyb3VwIDwtIGhlYXRtYXAuMihzdWJzZXQuZGlmZkdlbmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3J0Q29sID0gMCwgYWRqQ29sPSBjKDAuNSwwLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3d2PWFzLmRlbmRyb2dyYW0oc3Vic2V0LmNsdXN0Um93cyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvbHY9YXMuZGVuZHJvZ3JhbShzdWJzZXQuY2x1c3RDb2x1bW5zKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5LnRpdGxlID0gTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1haW4gPSBwYXN0ZTAoIkRFRyBIZWF0bWFwIChieSBsaWZlIHN0YWdlKTogIiwgeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YiA9IHBhc3RlMCgiVGhyZXNob2xkOiBwIDwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFkai5QLnRocmVzaCwgIjsgbG9nLWZvbGQgY2hhbmdlID4gIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxmYy50aHJlc2gpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3dTaWRlQ29sb3JzPXN1YnNldC5tb2R1bGUuY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1yZXYobXloZWF0Y29sb3JzKSwgc2NhbGU9J3JvdycsIGxhYlJvdz1OQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVuc2l0eS5pbmZvPSJub25lIiwgdHJhY2U9Im5vbmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXhSb3c9MSwgY2V4Q29sPTEpIAogIAogIG91dHB1dCA8LSBsaXN0KHN1YnNldC5kaWZmR2VuZXMuaGVhdG1hcC5ieWdyb3VwID0gc3Vic2V0LmRpZmZHZW5lcy5oZWF0bWFwLmJ5Z3JvdXApCiAgCn0KIyBOb3RlIHRoYXQgc2F2aW5nIHRoZXNlIGZpbGVzIGlzbid0IHJlYWxseSBoZWxwZnVsIGIvYyBoZWF0bWFwLjIgb2JqZWN0cyBjYW4ndCBiZSBlYXNpbHkgcmUtcGxvdHRlZDsgYXQgbGVhc3Qgbm90IHdpdGhvdXQgaGF2aW5nIGFsbCB0aGUgcmF3IGNvbXBvbmVudHMgb24gaGFuZC4KI291dHB1dC5jbHVzdGVyaW5nLlNzUk5Bc2VxIDwtIG1hcHBseShwYWlyd2lzZS5DbHVzdGVyaW5nLCBjb21wYXJpc29ucyR0YXJnZXRTdGFnZXMsIGNvbXBhcmlzb25zJGNvbnRyYXN0U3RhZ2VzLCBVU0UuTkFNRVMgPSBULCBTSU1QTElGWSA9IEZBTFNFKQojbmFtZXMob3V0cHV0LmNsdXN0ZXJpbmcuU3NSTkFzZXEpIDwtICBwYXN0ZShjb21wYXJpc29ucyR0YXJnZXRTdGFnZXMsIGNvbXBhcmlzb25zJGNvbnRyYXN0U3RhZ2VzLCBzZXAgPSAiLSIpCgpgYGAKIyBPdXRwdXQ6IEV4YW1wbGUgUGFpcndpc2UgQ29tcGFyaXNvbnM6IGlMMyB2cyBGTEYgLS0tCmBgYHtyIGh0bWxEaXNwbGF5X0RFR19wYWlyd2lzZSwgd2FybmluZz1GQUxTRX0Kb3V0cHV0LkRFRy5Tc1JOQXNlcSRgaUwzLUZMRmAkaW50ZXJhY3RpdmUudnBsb3QKb3V0cHV0LkRFRy5Tc1JOQXNlcSRgaUwzLUZMRmAkREVHLmRhdGF0YWJsZQpgYGAKCgojIFNhdmUgUGFpcndpc2UgQ29tcGFyaXNvbiBEYXRhIC0tLQpgYGB7ciBzYXZlRGF0YSwgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFfQojIEludHJvZHVjdGlvbiB0byB0aGlzIGNodW5rIC0tLS0KIyBTYXZpbmcgdGhlIERFR0xpc3QgY29udGFpbmluZyBzYW1wbGUgZGF0YSBhbmQgZ2VuZSBpbmZvcm1hdGlvbi4gCiMgQWxzbyBzYXZpbmcgYWxsIHRoZSBwYWlyd2lzZSBjb21wYXJpc29uIGNhbHVjbGF0aW9ucyBhbmQgcGxvdHMuCgojIFNhdmUgdGhlIGZpbHRlcmVkLCBub3JtYWxpemVkIERHRUxpc3Qgb2JqZWN0IC0tLS0Kc2F2ZShteURHRUxpc3QuZmlsdGVyZWQubm9ybSwKICAgICBmaWxlID0gIi4vT3V0cHV0cy9Tc0RHRUxpc3QiKQoKIyBKb2luIGFuZCBzYXZlIHRoZSBvdXRwdXQgbGlzdHMgb2YgREVHIElkZW50aWZpY2F0aW9uIGFuZCBDbHVzdGVyaW5nIGZ1bmN0aW9ucwpvdXRwdXQuU3NSTkFzZXEgPC0gTWFwKGMsIG91dHB1dC5ERUcuU3NSTkFzZXEsIG91dHB1dC5jbHVzdGVyaW5nLlNzUk5Bc2VxKQoKb3V0cHV0LlNzUk5Bc2VxW1siR2VuZXJhbCJdXSA8LSBsaXN0KGRpZmZHZW5lcy5hbGwuc3RhZ2VzID0gZGlmZkdlbmVzLmFjcm9zcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZmZHZW5lcy5oZWF0bWFwLmJ5Z3JvdXAgPSBkaWZmR2VuZXMuaGVhdG1hcC5ieWdyb3VwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG15c2NvcmVzLlRvcDEwID0gbXlzY29yZXMuVG9wMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQQ0EucGxvdCA9IHAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUENBLnNtYWxsLm11bHRpcGxlcyA9IHA2CikKCiMgU2F2ZSB0aGUgREVHIGRhdGF0YWJsZXMgZm9yIGFsbCB0aGUgcGFyaXdpc2UgY29tcGFyaXNvbnMsIGZvciB3b3JraW5nIHdpdGggYSBTaGlueSBBcHAKREVHcy5vbmx5PC0gc2FwcGx5KGNvbnRyYXN0cywgZnVuY3Rpb24oeCkge291dHB1dC5Tc1JOQXNlcVtbeF1dJERFRy5kYXRhdGFibGV9LCBVU0UuTkFNRVMgPSBUUlVFLCBzaW1wbGlmeSA9IEYpCiAgCiBzYXZlKERFR3Mub25seSwKICAgICAgZmlsZSA9ICIuL091dHB1dHMvU3NMaWZlU3RhZ2VDb21wYXJpc29uc19zdWJzZXQiKQpgYGAKCgo=